Spring Bootの@Validと@Validatedの違いを解説する記事のアイキャッチ。若手エンジニアがJava道場風の学習空間で入力チェックを学ぶイラスト。

実装・エラー解決

Spring Bootの@Validと@Validatedの違い|必須チェックが効かない原因と使い分け

AIによる要約

Spring Bootの@Validと@Validatedは、通常の入力検証とグループ指定で使い分けます。特に「@Validated(CreateGroup.class)へ変えたら@NotBlankが効かない」場合は、共通制約のDefaultグループが検証対象に含まれているかを確認してください。登録・更新の入力例と実行結果で違いを確かめます。

新人SE
新人SE
@Validと@Validatedは、どちらを使えばいいんですか?
ポンコツSE
ポンコツSE
普段の画面フォームやJSON入力なら、まず@Validで十分なことが多いです。@Validatedは、登録と更新でチェックを分けるなど、少し踏み込んだ場面で出てきます。

Spring Bootで入力チェックを調べると、@Validと@Validatedの両方が出てきます。

普通のFormやRequest DTOを検証するなら@Valid、登録と更新で制約を切り替えるなら@Validatedのグループ指定が候補です。ただし、指定を変えただけで、それまで動いていた名前の必須チェックが外れることがあります。まず使い分けを確認し、次にその見落としを再現します。

この記事のポイント

  • @Validは標準的なBean Validationの入口として使う
  • @ValidatedはSpringの検証機能で、グループ指定などに使える
  • 普通のForm入力ならまず@Validで考える
  • 既存プロジェクトの方針に合わせることも重要

@Validは標準的な入力チェックとして使う

@Validは、Bean Validationの仕組みを使って、FormクラスやRequest DTOに付けた制約を検証します。

public class UserRegisterForm {
    @NotBlank
    private String name;

    @NotBlank
    @Email
    private String email;
}

@PostMapping("/users")
public String register(@Valid UserRegisterForm form,
                       BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
        return "users/new";
    }
    userService.register(form);
    return "redirect:/users";
}

通常の登録フォームやJSON入力では、まずこの形を理解すれば十分です。必須、桁数、メール形式などの基本的なチェックは@Validで扱えます。

@ValidatedはSpringの検証アノテーション

@ValidatedはSpringが提供する検証用アノテーションです。代表的な使い道は、グループバリデーションです。

import jakarta.validation.groups.Default;

public interface CreateGroup extends Default {}
public interface UpdateGroup extends Default {}

public class UserForm {
    @Null(groups = CreateGroup.class)
    @NotNull(groups = UpdateGroup.class)
    private Long id;

    @NotBlank
    private String name;

    // Formとして受け取るため、getter / setterを用意する
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}

上のコードは3ファイル分を続けて示しています。CreateGroup.java、UpdateGroup.java、UserForm.javaへ分けて保存し、それぞれに必要なpackageとimportを付けてください。Defaultのimportは両方のグループに必要です。

登録時はID不要、更新時はID必須、というように同じFormでも処理ごとにチェックを変えたい場合に使われます。

@PostMapping("/users")
public String create(@Validated(CreateGroup.class) UserForm form,
                     BindingResult bindingResult) {
    // 登録用のチェック
    return "users/new";
}

@PostMapping("/users/{id}")
public String update(@Validated(UpdateGroup.class) UserForm form,
                     BindingResult bindingResult) {
    // 更新用のチェック
    return "users/edit";
}

@Validatedで@NotBlankが効かないときはDefaultを確認する

制約にgroupsを書かない場合、その制約はjakarta.validation.groups.Defaultへ属します。@Validated(CreateGroup.class)のようにグループを指定すると、無関係なDefaultが自動で追加されるわけではありません。

// 修正前:Defaultを継承していない
public interface CreateGroup {}

// この制約はDefaultグループに属する
@NotBlank
private String name;

// CreateGroupだけでは、上のnameは検証対象にならない
@Validated(CreateGroup.class)

上の最後の行はグループ指定を抜き出したものです。修正前の構成で、IDがnull・名前が空文字のFormをCreateGroupだけで検証すると、違反は0件になりました。@NotBlankを削除したり、画面側の必須入力だけに頼ったりせず、前のコードのようにCreateGroupとUpdateGroupをDefaultから継承させます。

グループを継承させない設計なら、@Validated({Default.class, CreateGroup.class})のように両方を明示する方法もあります。ここでは登録・更新の両方に共通の名前チェックが必要なので、継承する方法を採用しました。どちらか一方に統一してください。

登録・更新で実際に検証される項目

検証環境:Java 17.0.8、Spring Boot 3.5.16、Hibernate Validator 8.0.1.Final。以下の入力と結果は、実際にテストして確認したものです。プロジェクトの独自Validatorや例外処理を加えた場合は、その設定でも確認してください。

指定グループID名前検証結果
CreateGroupnullAlice違反なし
CreateGroupnull空文字name
CreateGroup10Aliceid
CreateGroup10空文字id・name
UpdateGroup10Alice違反なし
UpdateGroup10空文字name
UpdateGroupnullAliceid
UpdateGroupnull空文字id・name

表のid・nameは、制約違反が検出されたフィールドです。名前の必須チェックが両方のグループで動くことまで確認します。次は、Springから注入されたValidatorを使うJUnit 5テストの抜粋です。

@Autowired
Validator validator;

@Test
void 登録グループでも空の名前を拒否する() {
    UserForm form = new UserForm();
    form.setId(null);
    form.setName("");

    Set<String> fields = validator.validate(form, CreateGroup.class).stream()
            .map(v -> v.getPropertyPath().toString())
            .collect(Collectors.toSet());

    assertThat(fields).containsExactly("name");
}

主なimportはjakarta.validation.Validator、java.util.Set、java.util.stream.Collectors、JUnitのTest、AssertJのassertThatです。テスト依存を含むSpring Bootのテストクラスで実行します。制約のグループ選択と継承の仕様は、Hibernate Validatorの公式資料でも確認できます。

初学者はまず@Validで考える

新人のうちは、いきなり@Validatedやグループバリデーションを使いこなそうとしなくて大丈夫です。普通の入力チェックなら@Validで始める方が読みやすいです。

迷ったら、まず@Valid。登録と更新でチェックを分けたい、メソッド引数を検証したいなど、理由が出てきたら@Validatedを検討します。

importの違いにも注意する

Spring Boot 3系では、@Validはjakarta.validation.Validを使います。古い記事や古いプロジェクトではjavax.validation.Validが出てくることもあります。

import jakarta.validation.Valid;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;

import org.springframework.validation.annotation.Validated;

プロジェクトのSpring Bootバージョンによってimportが違うため、サンプルをコピペして赤くなったときは、まずimportを確認しましょう。

現場での使い分け

  • 単純な画面入力: @Valid
  • JSON APIのRequest DTO: @Valid
  • 登録と更新でチェックを切り替える: @Validatedを検討
  • Serviceメソッドの引数検証: プロジェクト方針を確認して@Validatedを検討

使い分けで大事なのは、難しい方を使うことではありません。チームのコードを読んだ人が、なぜそのアノテーションなのか理解できることです。

レビュー前のセルフチェック

確認リスト

  • 普通の入力チェックなのに、理由なく@Validatedを使っていないか
  • 登録と更新でチェックを分ける必要が本当にあるか
  • グループを指定したときも、共通の必須チェックが動くか
  • importがプロジェクトのSpring Bootバージョンに合っているか
  • 既存コードの入力チェック方針から外れていないか

この記事とあわせて読みたい

関連記事BindingResultの使い方と注意点|@Validの直後に置く理由 BindingResult は、 @Valid で検出した入力エラーをControllerで受け取るための引数です。重要なのは、 @Valid を付けたFormの直後に置くことです。 記事を読む 関連記事Spring Bootで入力チェックが重複する|ControllerとServiceの分け方 先に結論画面とAPIの両方に同じ重複チェックを書いているなら、メール重複などの業務判断をServiceへ集約します。 記事を読む 関連記事Spring BootのFormとDTOの違い|Serviceへ渡すクラスを分ける判断 先に結論FormをServiceへ直接渡してよいかは、画面の入力契約とServiceの業務処理の契約が同じかで判断します。 記事を読む

まとめ

@Validと@Validatedは、どちらも入力チェックに関係します。初学者は、まず@Validを標準的な入口として理解しましょう。

@Validatedは、グループバリデーションなど明確な理由があるときに使うと考えると、現場コードでも迷いにくくなります。

制約の使い分けが分かったら、エラー表示・入力値の保持・テストまで含めたSpring Bootのフォーム入力・バリデーション実践ガイドで、次に確認する範囲を整理できます。

関連記事Spring Bootのフォーム入力・バリデーション実践ガイド|実装からテストまで フォームを作って終わりにしない。入力の約束からエラー時の再表示、Serviceの業務判断、MockMvcでの確認まで、既存記事を読む順と確認する問いをまとめました。 記事を読む

Springの入力チェックをアプリの流れで学ぶ参考書

フォームの制約、Controllerでの検証、画面へのエラー表示を一つの流れで学びたい人には、『作って学ぶ Spring Boot入門』が候補です。Javaの基本文法、HTML/CSS、基本SQLが前提で、Java未経験者の最初の一冊ではありません。出版社の紹介・目次で、今学びたい範囲と合うか確認できます。

書籍「作って学ぶ Spring Boot入門」の表紙
商品画像:Amazon.co.jp
PR Java新人・若手向け

作って学ぶ Spring Boot入門

多田 真敏 (著)

Spring Bootを、動くWebアプリを作りながら覚える。

Controller、画面、DB連携などを一つのアプリとして実装できます。Java基礎からSpringの現場へ進む橋渡しに向く一冊です。

  • Spring Bootを手を動かして学びたい
  • Webアプリ開発の流れをつかみたい

当サイトはAmazonアソシエイト・プログラムの参加者です。価格・在庫・配送条件はAmazonでご確認ください。

-実装・エラー解決
-,