Javaの@Validアノテーションを徹底解説!初心者でもわかる入力値検証の基本
生徒
「Javaで、フォームの入力値が正しいかどうかを検証する方法ってありますか?」
先生
「はい、@Validアノテーションを使うと、入力値が正しいかどうかを自動的に検証できます。これは、バリデーションの強力なツールです。」
生徒
「それは便利ですね!でも、具体的にはどう使うんですか?」
先生
「それでは、@Validアノテーションを使った基本的な使い方を見ていきましょう!」
1. @Validアノテーションとは?
Javaの@Validアノテーションは、フォームの入力値を検証するためのものです。このアノテーションを使うことで、指定したクラスやオブジェクトが持っているプロパティ(フィールド)に対して、バリデーション(検証)を行うことができます。
@Validは、Spring Frameworkの一部として使われることが多く、特にフォームから送信されたデータが正しいかどうかをチェックする際に非常に役立ちます。例えば、ユーザーがフォームに名前やメールアドレスを入力したとき、その値が正しい形式かどうかを自動で検証することができます。
イメージとしては「@Valid=検査を有効化する合図」。検査の具体的な“ルール”は、クラスの各フィールドに付ける@NotBlankや@Emailなどの制約アノテーションが担い、@Validがそれらを一括で起動します。これにより、コントローラーで受け取ったオブジェクトが“正しい状態”かどうかを機械的に判断できます。
import jakarta.validation.constraints.*; // Spring Boot 3系の場合
public class UserForm {
@NotBlank(message = "名前は必須です")
private String name;
@Email(message = "メール形式が不正です")
private String email;
// getter / setter...
}
上のように“フィールド側”にルールを付けておき、コントローラーのパラメータに@Validを付けると、そのルールがまとめて適用されます(使い方の詳細は後の章で扱います)。まずは「ルール=フィールド、実行=@Valid」の分担だけ覚えておけば十分です。
- できること: 未入力チェック、書式チェック、文字数・数値範囲などを自動判定
- よく使う制約:
@NotBlank/@Email/@Size/@Min/@Maxなど - 覚え方:
@Validで「検査スタート」、結果は後段で取り出して表示やハンドリングに回す
2. @Validアノテーションの基本的な使い方
使い方はシンプルです。① 入力用クラスに“ルール”を付ける → ② コントローラで@Validを付けて受け取る → ③ BindingResultで結果を見る、の3ステップ。まずはフォーム送信(Thymeleaf等のPOST)を例に確認しましょう。
// ① 入力用クラス(DTO)に制約を付与
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Email;
public class UserForm {
@NotBlank(message = "名前は必須です")
private String name;
@Email(message = "正しいメールアドレスを入力してください")
private String email;
// getter / setter...
}
次に、@Validで受け取り、直後のBindingResultでエラー有無を判定します。“直後に置く”のがポイントです。
// ② ③ コントローラ(フォームPOSTの基本形)
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping("/user")
public class UserController {
@PostMapping("/submit")
public String submitForm(@Valid @ModelAttribute UserForm form, // ← @Validで検査を起動
BindingResult result) { // ← 必ず直後に置く
if (result.hasErrors()) {
// エラー時は入力画面に戻してメッセージ表示(次章の画面例と接続)
return "userForm";
}
// OKなら処理を進める(保存・画面遷移など)
return "redirect:/user/success";
}
}
@PostMapping("/api/submit")
public ResponseDto submitApi(@Valid @RequestBody UserForm form, BindingResult result) {
if (result.hasErrors()) { /* 400相当の応答を返す等 */ }
return new ResponseDto("ok");
}
Spring Boot 3系なら jakarta.validation.* に読み替え可。まずは「DTOに制約/@Validで実行/BindingResultで分岐」の流れを体で覚えましょう。
生徒
「BindingResultは、どうして@Validの直後じゃないとダメなんですか?」
先生
「直後でないと“どの引数の検証結果か”をSpringが正しく結び付けられないからです。順番違いだと例外になったり、結果を拾えません。」
生徒
「@ModelAttributeと@RequestBodyは何が違いますか?」
先生
「前者はフォーム(クエリ/フォームデータ)をオブジェクトへバインド、後者はJSONなどのボディをパースしてバインドします。どちらでも@Validは有効です。」
生徒
「@NotNullと@NotBlank、どちらを使えば?」
先生
「文字列の必須なら@NotBlank(空白のみNG)。@NotNullは“nullじゃなければOK”で空文字は通る点が違います。」
生徒
「エラーメッセージはどこで表示しますか?」
先生
「次章の画面例で出します。テンプレートではth:errorsで該当フィールドのメッセージを表示できます。」
生徒
「任意入力の項目はどう扱うのが自然ですか?」
先生
「必須系アノテーションを付けないだけでOK。必要があれば@Size(max = ...)など“範囲だけ”を与えます。」
生徒
「フロント側のHTML5バリデーションがあるなら、サーバ側は要りませんか?」
先生
「サーバ側は必須です。改ざんやAPI直叩きに備える最後の砦が@Validです。両輪で守りましょう。」
3. バリデーションエラーの表示方法
画面では「どの項目に何のエラーがあるか」をはっきり示すのがコツです。Thymeleafではフォーム全体に対してth:objectで対象オブジェクト(例:userForm)を関連付け、各入力にth:field="*{...}"を付けると、自動で値の再表示とエラーのひも付けが行えます。フィールド単位のメッセージはth:errors、有効性の整合性などに使う「全体エラー」は#fields.globalErrors()で扱えます。
<!-- userForm を対象にする(コントローラで model.addAttribute("userForm", new UserForm()) 等) -->
<form th:action="@{/user/submit}" method="post" th:object="${userForm}" class="needs-validation">
<!-- フォーム全体のエラーをまとめて表示(例:名前とメールの組合せ重複など) -->
<div class="alert alert-danger" th:if="${#fields.hasGlobalErrors()}">
<ul class="mb-0">
<li th:each="e : ${#fields.globalErrors()}">[[${e}]]</li>
</ul>
</div>
<div class="mb-3" th:classappend="${#fields.hasErrors('name')} ? 'has-validation' : ''">
<label for="name" class="form-label">名前</label>
<input id="name" type="text" class="form-control" th:field="*{name}" />
<div class="invalid-feedback d-block" th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></div>
</div>
<div class="mb-3" th:classappend="${#fields.hasErrors('email')} ? 'has-validation' : ''">
<label for="email" class="form-label">メールアドレス</label>
<input id="email" type="email" class="form-control" th:field="*{email}" />
<div class="invalid-feedback d-block" th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></div>
</div>
<button type="submit" class="btn btn-primary">送信</button>
</form>
上の例では、入力欄にth:fieldを付けるだけで「前回入力した値の保持」「該当フィールドのエラー取得」がセットで効きます。フィールドに問題があるときは#fields.hasErrors('name')で検出し、th:errors="*{name}"でメッセージを表示します。フォーム全体の整合性チェック(重複・相関など)はglobalErrorsとして上部にまとめて出すと親切です。
th:objectを付けると、*{フィールド名}で簡潔に書けます(先頭の*は“今のオブジェクト”の意味)。- サーバ側でエラーがあると、
BindingResultがメッセージを保持し、上記のth:errorsで自動表示されます。 - 見た目調整はBootstrapの
invalid-feedback等を併用すると分かりやすくなります。
生徒
「入力値は自動で残りますか?」
先生 「はい。th:fieldを使えば再表示時も自動で値が入ります。エラー箇所だけ修正すればOKです。」
4. @Validの注意点
@Validは「付ければ自動で全部やってくれる魔法」ではありません。実務でつまずきやすいポイントを先に整理しておきましょう。ここを押さえておくと、次章以降(画面表示/国際化/グループ検証)もすっと入ります。
Spring Bootでは spring-boot-starter-validation が必要です。Boot 3 系は jakarta.validation.*、Boot 2 系は javax.validation.* パッケージになります(どちらも使い方は同じ感覚でOK)。
<!-- Maven(例) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
フォーム(@ModelAttribute)を @Valid で受けるとき、直後に BindingResult を置きます。離れていると検証結果を受け取れず例外になることがあります。
// ✅ 正しい
public String submit(@Valid @ModelAttribute UserForm form, BindingResult result) { ... }
// ❌ NG(間に別引数を挟む)
public String submit(@Valid @ModelAttribute UserForm form, Locale loc, BindingResult result) { ... }
※APIでJSONを受ける @RequestBody の場合は、例外(MethodArgumentNotValidException)として扱うのが定番。フォームは BindingResult、APIは例外ハンドリング、と覚えると混乱しません。
@NotBlank:文字列専用。「空文字」「空白だけ」もNG(ユーザー入力の必須はまずコレ)。@NotEmpty:空コレクションや空文字をNGにしたいとき。@NotNull:null でなければOK(空文字は通る)。
テンプレート側では th:object と th:field を使うと、再表示時の値保持とフィールドエラー表示が自動で結び付きます(前章のフォーム例とセットで使います)。
<form th:object="${userForm}">
<input th:field="*{name}">
<div th:errors="*{name}"></div>
</form>
子オブジェクトやリスト要素まで検証したいときは、親のフィールドに @Valid を付けます(詳しくは後章「カスケード検証」で解説)。
メッセージはハードコードより ValidationMessages.properties で管理すると、多言語対応・再利用・文言統一が楽になります(詳しくは後章「メッセージの国際化」で扱います)。
@Validは「入力値がルールに合っているか」の判定まで。業務的な重複チェックや外部サービス照合などは、アプリ側のロジックやサービス層で行います。(軽い構文チェック=@Valid、重い業務チェック=アプリ)
生徒
「とりあえず @Valid を付ければ大丈夫ですか?」
先生 「土台はできますが、BindingResult の位置、必須アノテーションの選び方、テンプレートの結び付けを押さえるのが“実務で効く”コツです。」
5. @Validatedとの違いとグループバリデーション(Create/Updateで検証ルールを切替)
@Validは「すべての制約を実行」するのに対し、@Validatedは「検証グループ」を指定して、場面(新規作成・更新)ごとに制約を切り替えられます。Javaの@Validアノテーション 使い方/入力値検証の基本/Spring Boot バリデーションというSEO観点の主要キーワードにも合致する重要ポイントです。
// 検証グループのマーカーインターフェース
public interface Create {}
public interface Update {}
// DTO(場面ごとに制約を切替)
import javax.validation.constraints.*;
public class AccountForm {
@NotBlank(groups = {Create.class, Update.class}, message = "ユーザー名は必須です")
private String username;
// 新規作成時だけ必須、更新時は任意
@NotBlank(groups = Create.class, message = "パスワードは新規作成時に必須です")
private String password;
// 更新時のみ必須
@NotBlank(groups = Update.class, message = "表示名は更新時に必須です")
private String displayName;
// getter/setter...
}
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping("/account")
public class AccountController {
@PostMapping("/create")
public String create(@Validated(Create.class) @ModelAttribute AccountForm form,
BindingResult result) {
if (result.hasErrors()) return "accountForm";
return "redirect:/account/created";
}
@PostMapping("/update")
public String update(@Validated(Update.class) @ModelAttribute AccountForm form,
BindingResult result) {
if (result.hasErrors()) return "accountForm";
return "redirect:/account/updated";
}
}
メモ:Spring Boot 3 以降は jakarta.validation.* パッケージに変更されています(javax.validation.*と読み替え可)。
6. ネストオブジェクト/コレクションの入れ子検証(@Validのカスケード)
フォームが入れ子構造の場合、親フィールドに@Validを付けると、子オブジェクトやコレクション要素まで自動的に再帰検証(カスケード)されます。これにより、住所や明細行など複合入力の入力値検証を簡潔に保てます。
// 子オブジェクト
import javax.validation.constraints.*;
public class Address {
@NotBlank(message = "都道府県は必須です")
private String prefecture;
@NotBlank(message = "市区町村は必須です")
private String city;
@Pattern(regexp = "\\d{3}-\\d{4}", message = "郵便番号はXXX-XXXX形式で入力してください")
private String zip;
// getter/setter...
}
// コレクション要素
public class Phone {
@Pattern(regexp = "0\\d{1,4}-\\d{1,4}-\\d{4}", message = "電話番号の形式が正しくありません")
private String number;
// getter/setter...
}
// 親フォーム
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
public class ProfileForm {
@Valid // 子まで検証
private Address address = new Address();
@Valid // 要素ごとに検証
private List<Phone> phones = new ArrayList<>();
// getter/setter...
}
@PostMapping("/profile/save")
public String saveProfile(@Valid @ModelAttribute ProfileForm form, BindingResult result) {
if (result.hasErrors()) {
// address.*, phones[i].* のエラーもここに格納される
return "profileForm";
}
return "redirect:/profile?ok";
}
画面側では th:errors="*{address.zip}" や th:each で phones の各要素エラーを表示します。大規模フォームでも一貫した@Valid 使い方で保守性が上がります。
7. メッセージの国際化・カスタムメッセージ(ValidationMessages)
バリデーション文言はハードコードせず、ValidationMessages.properties(多言語は ValidationMessages_ja.properties など)に定義します。Javaの@Validアノテーション メッセージ/エラー表示 カスタマイズというSEOキーワードにも有効です。
// DTO(メッセージキーを参照)
import javax.validation.constraints.*;
public class UserForm {
@NotBlank(message = "{user.name.required}")
private String name;
@Email(message = "{user.email.invalid}")
private String email;
// getter/setter...
}
# src/main/resources/ValidationMessages.properties
user.name.required=名前は必須です
user.email.invalid=正しいメールアドレスを入力してください
# 任意(多言語化する場合)
# src/main/resources/ValidationMessages_en.properties
user.name.required=Name is required.
user.email.invalid=Please enter a valid email address.
# (必要に応じて)application.properties
spring.messages.basename=messages,ValidationMessages
Bean Validationはデフォルトで ValidationMessages を参照します。フィールド名の見栄えを変えたい場合は、個別キー化(例:user.name.required)やプレースホルダ({min} など)を活用すると、再利用性と可読性が向上します。
まとめ
Javaの@Validアノテーションは、Spring BootやSpring MVCでフォーム入力やリクエストデータを受け取るときに、入力値が決められたルールを満たしているか自動的に検証するための重要な仕組みです。利用者が入力した名前、メールアドレス、住所、電話番号などは、そのまま処理へ進めるのではなく、必須項目が入力されているか、文字数が範囲内か、メールアドレスの形式が正しいかなどを確認する必要があります。@Validを使うことで、こうした入力値検証をコントローラーの中へ大量に書かず、入力用クラスに定義したバリデーションルールをまとめて実行できます。
基本的な考え方は、入力用のフォームクラスやDTOのフィールドへ@NotBlank、@Email、@Size、@Min、@Maxなどの制約アノテーションを付け、コントローラーで受け取る引数へ@Validを指定するという流れです。制約アノテーションが検証ルールを定義し、@Validがその検証を実行する合図になると考えると分かりやすくなります。Spring Bootでフォーム入力を安全に扱うためには、この役割分担を最初に理解しておくことが大切です。
@Validを使った入力値検証の基本を整理しよう
@Validを使うときは、まず検証対象となるクラスを用意します。たとえば利用者登録フォームで名前とメールアドレスを入力してもらう場合、名前には未入力や空白だけの入力を防ぐ@NotBlank、メールアドレスには形式を確認する@Emailを付けることができます。入力ルールをフォームクラスへまとめることで、どの項目にどの条件が必要なのかをコードから確認しやすくなります。
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
public class UserForm {
@NotBlank(message = "名前は必須です")
private String name;
@NotBlank(message = "メールアドレスは必須です")
@Email(message = "正しいメールアドレスを入力してください")
private String email;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
この例では、名前とメールアドレスの入力条件をUserFormへまとめています。コントローラー側で同じ条件を何度も判定する必要がなくなり、入力値検証の責任をフォームクラスへ集約できます。フォーム項目が増えた場合でも、それぞれのフィールドへ必要な制約を追加していけばよいため、バリデーションルールを確認しやすくなります。
@ValidとBindingResultはセットで考えよう
Spring MVCのフォーム処理では、@Validで検証を実行したあと、その結果をBindingResultで受け取ります。BindingResultには、どのフィールドでエラーが発生したか、どのようなエラーメッセージが設定されているかといった情報が格納されます。hasErrorsを使えば、ひとつでも入力エラーがあるかどうかを簡単に確認できます。
import jakarta.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class UserController {
@PostMapping("/user/submit")
public String submit(
@Valid @ModelAttribute UserForm form,
BindingResult result) {
if (result.hasErrors()) {
return "userForm";
}
return "redirect:/user/success";
}
}
ここで重要なのは、フォームオブジェクトの直後にBindingResultを置くことです。Springがどの検証結果をどのBindingResultへ渡すのかを正しく判断できるようにするため、引数の順番を意識する必要があります。入力エラーがあれば入力画面へ戻し、問題がなければ保存処理や次の画面への遷移へ進むという流れが、Spring Bootのフォームバリデーションでよく使われる基本形です。
NotBlankとNotNullとNotEmptyの違いを理解しよう
入力値検証では、必須チェックに使うアノテーションの違いを理解することも重要です。文字列の入力フォームでは@NotBlankがよく使われます。これは値が存在するだけではなく、空文字や空白だけの入力も不正として扱います。利用者が名前欄へ空白だけを入力した場合にもエラーにできるため、文字列の必須入力と相性がよい制約です。
@NotNullは、値がnullでないことを確認する制約です。そのため文字列が空文字であっても、値そのものが存在すれば条件を満たす場合があります。@NotEmptyは空文字や空のコレクションなどを許可したくない場合に利用できます。名前が似ているため初心者が混乱しやすい部分ですが、文字列の入力必須なら@NotBlank、単純にnullを禁止したいなら@NotNullというように、検証したい内容から選ぶことが大切です。
Thymeleafと組み合わせるとエラーメッセージを表示しやすい
Spring BootでThymeleafを利用している場合は、th:object、th:field、th:errorsなどを使うことで、フォームオブジェクトとバリデーションエラーを画面へ結び付けられます。入力エラーが発生した場合でも、利用者が入力した内容を残した状態でフォームを再表示し、どの項目を修正すればよいのか分かるようにエラーメッセージを表示できます。
画面側では、対象となるフォームオブジェクトをth:objectで指定し、入力欄をth:fieldで結び付けます。そしてエラー表示にはth:errorsを利用します。これにより、Java側で定義した制約アノテーションのメッセージと画面上の入力欄を連携させられます。Bootstrapのinvalid-feedbackなどを組み合わせれば、エラーのある項目を利用者に分かりやすく伝える画面も作成できます。
HTML側の必須チェックや入力形式の指定も利用者の入力ミスを減らすために役立ちますが、サーバー側のバリデーションを省略してよいわけではありません。ブラウザ側の検証を通らずにリクエストが送信される可能性もあるため、Spring Boot側でも@Validを使って入力値を検証することが重要です。画面側の入力支援とサーバー側の入力値検証を組み合わせることで、より安全なフォーム処理を作れます。
@Validatedとの違いは検証グループで考えよう
@Validとよく一緒に学ぶものに@Validatedがあります。通常の入力値検証では@Validで十分な場面が多いですが、新規登録と更新で異なるルールを適用したい場合には、検証グループを指定できる@Validatedが便利です。たとえば新規登録ではパスワードを必須にし、更新ではパスワードを任意にしたいといった場合に、同じフォームクラスでも処理ごとに検証する制約を切り替えられます。
検証グループを使う場合は、作成用や更新用のマーカーインターフェースを用意し、各制約アノテーションのgroupsへ対象を指定します。そしてコントローラー側では@Validatedに利用するグループを指定します。入力フォームが単純なうちは無理に使う必要はありませんが、登録画面と編集画面で検証条件が異なるようになったときに覚えておくと便利な仕組みです。
入れ子になったオブジェクトではカスケード検証を使う
フォームが大きくなると、一つのクラスだけでなく、住所や電話番号などを別のオブジェクトとして持つことがあります。そのような入れ子構造では、親オブジェクトだけを検証しても、子オブジェクトの制約まで自動的に確認されない場合があります。そこで、親クラスが持つ子オブジェクトのフィールドへ@Validを付けることで、子オブジェクトまで続けて検証できます。
この仕組みはカスケード検証と呼ばれ、住所オブジェクトの都道府県や市区町村、電話番号リストの各要素などをまとめて検証したい場合に役立ちます。親フォームから子フォームへ検証を連鎖させることで、大きな入力画面でも同じ考え方でバリデーションを整理できます。
バリデーションメッセージはまとめて管理すると保守しやすい
制約アノテーションのmessageへ日本語の文章を直接書く方法は分かりやすいですが、フォームや入力項目が増えると同じ文言が何か所にも書かれるようになります。そのような場合は、ValidationMessages.propertiesなどへメッセージをまとめる方法が便利です。
メッセージを外部ファイルへまとめておけば、入力エラーの文章を変更したいときにJavaクラスを一つずつ探す必要がありません。また、日本語と英語など複数の言語へ対応するときにも管理しやすくなります。小さなサンプルでは直接メッセージを書き、大きなWebアプリケーションではメッセージファイルへ整理するというように、規模に合わせて使い分けるとよいでしょう。
@Validに任せる検証と業務処理を分けよう
@Validは、入力された値が決められた形式や範囲を満たしているか確認する処理に向いています。名前が未入力ではないか、メールアドレスの形式が正しいか、文字数が上限を超えていないか、数値が指定範囲に収まっているかといった検証です。一方で、同じメールアドレスがすでにデータベースへ登録されていないか、利用者が特定のサービスを利用できる状態かといった業務上の条件は、Serviceなどのアプリケーション側で確認する必要があります。
すべてを制約アノテーションだけで処理しようとすると、入力形式のチェックと業務ロジックの境界が分かりにくくなります。フォームの形式的な検証はBean Validation、データベース照合や業務ルールの判定はServiceというように役割を分けることで、コードの責任が明確になり、修正しやすいSpring Bootアプリケーションになります。
Spring Bootのバージョンによるパッケージの違いにも注意しよう
バリデーションを利用するときは、プロジェクトで使用しているSpring Bootの世代によってインポートするパッケージが異なる点にも注意します。Spring Bootの新しい環境ではjakarta.validationを利用し、以前の環境ではjavax.validationが使われています。インターネットの記事や古いサンプルコードを参考にしたときに、アノテーションが見つからない場合は、このパッケージの違いを確認すると解決できることがあります。
また、Spring Bootでバリデーションを利用するには、必要な依存関係がプロジェクトへ追加されていることも確認します。@Validや@NotBlankを書いたのに検証が動かない場合は、コードだけでなく依存関係やインポート先も確認することが大切です。エラーが発生したときに、アノテーションの書き方、依存関係、パッケージ、コントローラーの引数順というように順番に確認すると原因を探しやすくなります。
@Validを使ったフォーム処理の流れを身に付けよう
Spring Bootで@Validを使った入力値検証を実装するときは、まずフォームクラスへ制約アノテーションを定義し、コントローラーで@Validを付けて受け取り、BindingResultでエラーを確認します。エラーがあれば入力画面へ戻し、Thymeleafで入力値とメッセージを再表示します。問題がなければServiceなどの処理へ進み、必要に応じてデータベースへの保存や更新を行います。
この一連の流れを理解しておけば、会員登録フォーム、お問い合わせフォーム、ログイン関連の入力画面、商品登録、プロフィール編集など、さまざまなWebアプリケーションへ応用できます。最初は名前とメールアドレスだけの小さなフォームで練習し、その後で文字数チェック、数値範囲、正規表現、検証グループ、入れ子オブジェクト、メッセージの外部化へ進むと理解しやすくなります。
Javaの@Validアノテーションを学ぶうえで大切なのは、単にアノテーションを一つ付ければ終わりだと考えないことです。制約をどこへ定義するのか、検証をどこで実行するのか、結果をどこで受け取るのか、エラーをどのように画面へ表示するのかまでを一つの流れとして理解する必要があります。@Valid、BindingResult、制約アノテーション、Thymeleafのエラー表示を組み合わせて理解すると、Spring Bootにおける入力値検証の基本がしっかり身に付きます。
生徒
「今回の記事で、@Validは入力ルールそのものではなく、フォームクラスに書いたバリデーションを実行するための合図だと分かりました。」
先生
「その理解で大丈夫です。名前の必須チェックなら@NotBlank、メール形式なら@Emailというようにルールをフィールドへ定義して、@Validでまとめて検証します。」
生徒
「そしてコントローラーでは、@Validを付けたフォームの直後にBindingResultを書いて、エラーがあるか確認するんですね。」
先生
「はい。result.hasErrors()を確認して、エラーがあれば入力画面へ戻し、問題がなければ次の処理へ進むという流れが基本です。」
生徒
「必須チェックでも@NotBlank、@NotNull、@NotEmptyでは意味が違うことも覚えておく必要がありますね。」
先生
「そうですね。特に文字列の入力フォームでは、空白だけの入力も防げる@NotBlankが使いやすい場面が多いです。何を禁止したいのかを考えて制約を選びましょう。」
生徒
「Thymeleafではth:fieldとth:errorsを使うと、入力した値を残しながらエラーメッセージを表示できるんですよね。」
先生
「はい。利用者がどこを直せばよいのか分かる画面にすることも、バリデーションでは大切です。サーバーで検証するだけではなく、エラーを分かりやすく伝えるところまで考えましょう。」
生徒
「新規登録と更新でルールを変えたい場合は、@Validatedと検証グループを使う方法もあるんですね。」
先生
「そのとおりです。単純なフォームでは@Validから始めて、処理ごとに制約を切り替える必要が出てきたら検証グループを学ぶとよいでしょう。」
生徒
「住所のような子オブジェクトにも@Validを付ければ、中にある項目まで検証できるということですね。」
先生
「はい。それがカスケード検証です。フォームが大きくなっても、役割ごとにオブジェクトを分けながら入力値検証を続けられます。」
生徒
「メールアドレスの重複確認みたいな処理も@Validだけに任せればいいですか。」
先生
「入力形式のチェックと業務上のチェックは分けて考えましょう。形式や文字数などはバリデーションで確認し、データベースを使った重複確認などはService側で処理すると役割が分かりやすくなります。」
生徒
「古いサンプルでjavax.validation、新しいサンプルでjakarta.validationになっている理由も分かりました。」
先生
「プロジェクトで使っているSpring Bootの環境に合わせて確認してください。コードが似ていても、インポート先が違うとそのままでは利用できないことがあります。」
生徒
「まずは小さなフォームで、制約アノテーション、@Valid、BindingResult、Thymeleafのエラー表示まで一通り作ってみます。」
先生
「その方法が分かりやすいです。入力、検証、エラー判定、再表示、正常処理という一連の流れを自分で作れるようになれば、Spring Bootのフォームバリデーションをさまざまな画面へ応用できるようになります。」