データベースを使用して入力値をチェックする

データベースの状態を用いた入力値のチェックをしたい場合、バリデーションを実装する必要があります。

Tip

例えばバリデーションで重複登録のチェックを行ったとしても、同時に複数の登録処理が行われてしまうとバリデーションのタイミングでエラーにならないというようなこともありえます。バリデーションだけでデータの不整合を確実に防げるわけではありませんので、データベースの制約を使用する等、データの不整合が発生しないよう十分に注意してください。

以下のサンプルコードの動作確認環境については、 動作確認環境と依存ライブラリについて を参照してください。

実装例

Serviceで送出した例外をControllerで捕捉し、画面にエラーメッセージを表示します。

Controller
    @PostMapping("/")
    public ModelAndView add(@Valid @ModelAttribute Form form, BindingResult bindingResult,
                            RedirectAttributes redirectAttributes) {
        if (bindingResult.hasErrors()) {
            return createValidationErrorResponse();
        }
        try {
            userService.insert(new User(form.getName(), form.getMailAddress().getValue(), form.getRole()));
        } catch (UserService.RoleNotFoundException e) {
            // ロールがデータベースのロールテーブル上に存在しない例外を捕捉して
            // 画面にメッセージを表示します。
            bindingResult.rejectValue("role", "role.notFound");
            return createValidationErrorResponse();
        } catch (DuplicateKeyException e) {
            // 同じメールアドレスがデータベースに登録済みの場合には
            // 重複エラーが送出されるのでそれを元に画面にメッセージを表示します。
            bindingResult.rejectValue("mailAddress", "mailAddress.duplicated");
            return createValidationErrorResponse();
        }

        // message-source-start
        // MessageSourceから完了メッセージを取得して、Flashスコープに設定します。
        // プレースホルダに、「ユーザ」を設定します。
        redirectAttributes.addFlashAttribute("successMessage",
                messageSource.getMessage("add.success", new String[]{"ユーザ"}, LocaleContextHolder.getLocale()));
        // message-source-end

        return new ModelAndView("redirect:/");
    }
    
    private ModelAndView createValidationErrorResponse() {
        return new ModelAndView("index", HttpStatus.BAD_REQUEST);
    }
Service
    @Transactional
    public void insert(User user) {
        // ロールの存在チェック
        // 本来は外部キーの制約違反でハンドリングすべきですが、
        // ここでは実装例を示すためこのような実装にしています。
        if (!roleDao.existsRole(user.role)) {
            throw new RoleNotFoundException();
        }
        userDao.insert(user);
    }
メッセージ定義(messages.properties)
typeMismatch.java.lang.Integer=数値で入力してください。
typeMismatch.keel.validation.value.MailAddress=メールアドレス形式(sample@example.com)で入力してください。
role.notFound=ロールにはadmin以外登録出来ません。
mailAddress.duplicated=入力されたメールアドレスは登録済みです。
add.success={0}の登録を完了しました。

サンプル全体は validation-sample を参照してください。