Doma2の楽観ロックで排他制御する
Doma2の@Versionアノテーションを使用した楽観ロック方式で実現します。
Doma2の楽観ロックについては、以下の公式ドキュメントを参照してください。
以下のサンプルコードの動作確認環境については、 動作確認環境と依存ライブラリについて を参照してください。
処理フロー
初期表示
画面表示するデータをDBから取得します。
取得したデータをセッションに格納します。
更新
画面の入力値をバリデーションします。
バリデーション後の入力値とセッションに格納したバージョン番号を使用して、DBのデータを更新します。
データの更新に成功した場合は、初期表示にリダイレクトします。
楽観ロック例外が発生した場合は、HTTPステータスコードに409を設定して、エラーページを表示します。
実装例
Controllerでの userService.update(User)
の実行時にDoma2の楽観排他制御が行われます。楽観排他に失敗した場合は、Doma2がOptimisticLockExceptionを送出します。
OptimisticLockExceptionは、 doma-spring-boot-starter
がSpringのOptimisticLockingFailureExceptionに変換して再送出します。
詳細は、 Doma2が送出する例外の変換 を参照してください。
送出されたOptimisticLockingFailureExceptionはアプリケーション全体で横断的に処理することで、個別の機能で例外処理する必要がなくなります。詳細は、 例外ハンドリング を参照してください。
- Controller
@Controller @RequestMapping("/user") @SessionAttributes(names = "form") // Formクラスをセッションに格納します public class UserUpdateController { private final UserService userService; public UserUpdateController(UserService userService) { this.userService = userService; } @ModelAttribute(name = "form") public UserUpdateForm setup() { return new UserUpdateForm(); } @GetMapping("/edit") public String edit(@ModelAttribute(name = "form") UserUpdateForm form) { User user = userService.find(); // セッションに格納されているFormクラスに取得した情報を設定します form.setUserId(user.userId); form.setUserName(user.userName); form.setVersionNo(user.versionNo); return "user/edit"; } @PostMapping("/update") public String update(@ModelAttribute(name = "form") @Validated UserUpdateForm form, BindingResult bindingResult) { if (bindingResult.hasErrors()) { return "user/edit"; } // 画面の入力値と、セッションに格納されている情報を使用してデータを更新します userService.update(new UserDto(form.getUserId(), form.getUserName(), form.getVersionNo())); // データ更新に成功した場合は、初期表示にリダイレクトします return "redirect:/user/edit?success"; } }
- Dao
/** * 楽観ロックエラーが発生した場合は、{@link org.springframework.dao.OptimisticLockingFailureException}が送出されます。 * 呼出し元で例外を補足してハンドリングしてください。 * <p/> * なお、サンプルアプリケーションでは{@link org.springframework.web.bind.annotation.ControllerAdvice}を使用して、 * 全てのControllerを横断したエラーハンドリングを実施しています。 * @see keel.controller.ErrorControllerAdvice */ @Update Result<User> update(User user);
- Entity
// 楽観ロック用のバージョンカラムには、@Versionを付与します。 @Version public final Long versionNo;
- ControllerAdvice
@ControllerAdvice public class ErrorControllerAdvice { private final Logger LOGGER = LoggerFactory.getLogger(ErrorControllerAdvice.class); /** * 楽観ロック例外が発生した場合は、HTTPステータスコードに409を設定します。 * * @param e 楽観ロック例外 */ @ExceptionHandler(OptimisticLockingFailureException.class) @ResponseStatus(value = HttpStatus.CONFLICT, reason = "楽観排他エラー") public void optimisticLockingFailureExceptionHandler(OptimisticLockingFailureException e) { LOGGER.debug("排他制御エラーが発生しました", e); } }
サンプル全体は doma2-optimistic-lock-sample を参照してください。