0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Jakarta Validation】List に対する検証にも項目名つきメッセージを・・・!

0
Last updated at Posted at 2026-04-13

結論

ecuacion-lib-validation と言う Jakarta Validation ベースのライブラリには、List などの Collection や 配列の要素に対する validation で、項目名つきメッセージを出力する機能があります、と言うお話。

前提知識

ExceptionUtil の理解を前提の記事とさせていただいております m(_ _ )m
ecuacion-lib-validation の導入方法含め、下記記事を先にご覧くださいませ。

  • ExceptionUtil とそれを使用したメッセージへの項目名追加
    (導入方法は「ecuacion-lib-validation の導入方法」の項をご参照ください)

  • 項目名の有無のデフォルト設定と個別指定

はじめに

Bean Validation 改め Jakarta Validation では、List に対する validation も可能です。

例えば、こう言う validation ですね。
それぞれの社員が、複数のノートPCを保持する、と。model は機種名のことです。

Employee.java
public record Employee(String name,  List<@Valid Laptop> laptopList) {
  
}
Laptop.java
public record Laptop(int id, @NotNull String model) {

}

EmployeeList<Laptop>@Valid が付加されているので、List の要素である各 laptop に対しても validation が実施されます。

そして、validation を実行するコードはこちら。

Main.java(一部抜粋)
  public static void 基本的な使い方() {
    List<Laptop> laptopList = new ArrayList<>();
    laptopList.add(new Laptop(1, null));
    Set<ConstraintViolation<Employee>> set = validator.validate(new Employee("John", laptopList));

    for (String message : ExceptionUtil.getMessageList(set)) {
      System.out.println(message);
    }
  }

これを普通に実行した結果は、当たり前ですが以下。

実行結果
入力必須です

これではなんだかわからないので項目名を出したいのですが、さて、List の要素から発生するエラーのメッセージにおける項目名は、一体どのような文言にしたら良いのでしょう・・・?

List の要素のエラーでのわかりやすいメッセージ

まず第一歩は、こちら・・・?

機種名は入力必須です

普通に項目名を入れるとこうなる。でもこれではわからない。

ノートPCは複数持てるのでどのPCかの明示が必要なのと、他に携帯も持っていて携帯の機種名もあると、さらにわからない。

それらを考慮した、人が見てわかるメッセージとは、以下の感じかなと。

1番目のノートPCの機種名は入力必須です

ちなみに数字だけ見るとわかりませんが、コンピュータ上「0番目」で表されるものを、「1番目の〜」というメッセージで表現しているつもりです。つまり1番始まり。
人間には絶対そっちの方がわかりやすいよね・・・

可能ならこうしたいけど・・・

ちなみに、必ず入力されていることがわかる項目があれば、それを表示するのが一番わかりやすいです。
たとえばこんな。

ID=1のノートPCの機種名は入力必須です

でも、新規登録の場合などはID項目含めて空欄の場合もあるし、どの項目をメッセージに表示するか、の情報も必要になり複雑なので、実用性も鑑みると「1つめの〜」くらいの表現が妥当かと思われます。

validationの分割による解決

メッセージのイメージがついたところで、具体的な実装について。

先に言っておくと、綺麗にカスタマイズしたメッセージを出したい場合は、validation を分割実施する方法があります。

employeelaptop の validation を別で実施し、laptop の validation を実施する際に、わかりやすいメッセージを付加する、と言う方法です。

Main.java(一部抜粋)
  private static void validationの分割による解決() {
    try {
      EmployeeWithoutValid employee =
          new EmployeeWithoutValid("John", List.of(new Laptop(1, null)));

      // employee の validation
      ValidationUtil.validateThenThrow(employee);

      // laptop の validation
      for (int i = 0; i < employee.laptopList().size(); i++) {
        MessageParameters params =
            ValidationUtil.messageParameters().messagePrefix((i + 1) + "番目のノートPCについて、");
        ValidationUtil.validateThenThrow(employee.laptopList().get(i), params);
      }

    } catch (ConstraintViolationException ex) {
      for (String message : ExceptionUtil.getMessageList(ex, true)) {
        System.out.println(message);
      }
    }
  }

実行結果はこちら。

実行結果
1番目のノートPCについて、「機種」は入力必須です。

ただし、上記例だと employeelaptop の両方でエラーがあった場合に両方のエラーを同時にthrow することができません。

それも、それぞれで発生した ConstraintViolation を merge してから throw すれば対応は可能ではあります。

ただ、毎回これを書かなきゃいけないのは面倒ですね・・・

ValidationUtilExceptionUtil でのメッセージ出力

ValidationUtilExceptionUtil では、こんな List の validation に対応したメッセージを出力する機能があります。

基本的な使い方

validation を実行するためのコードはこちら。

Main.java(一部抜粋)
  public static void 基本的な使い方() {
    Employee employee = new Employee("John", List.of(new Laptop(1, null)));

    try {
      MessageParameters params = ValidationUtil.messageParameters().showsItemNamePath(true);
      ValidationUtil.validateThenThrow(employee, params);

    } catch (ConstraintViolationException ex) {
      for (String message : ExceptionUtil.getMessageList(ex, true)) {
        System.out.println(message);
      }
    }
  }

showsItemNamePath という parameter を指定しています。itemName の経路の表示有無設定ですね。true にすると表示されます。

この指定は、{0} があったときに初めて機能するものなので、併せて ExceptionUtil.getMessageList の引数に、項目名を表示するための true を指定しています。

結果、以下のようなメッセージが得られます。

実行結果
「employee.laptopList」の1番目の要素の「laptop.model」は入力必須です。

「」で括られた itemNameKey は、 messages.properties または item_names.properties にこのキーで定義をすることでラベル名が表示されます。

messages.properties または item_names.properties
laptop.model=機種
employee.laptopList=社員保持ノートPC(複数)

すると、結果はこうなります。

実行結果
「社員保持ノートPC(複数)」の1番目の要素の「機種」は入力必須です。

showsItemNamePathfalse を指定した(=デフォルト)場合

showsItemNamePath に唐突感があったので、もう少し補足しておきます。

showsItemNamePathfalse を設定した場合(あるいはそもそも showsItemNamePath の設定をしない場合)の実行結果は以下になります。

実行結果
「機種」は入力必須です。

ここでの「機種」は、「社員が保持する」という文脈を無視した、laptop.model に対する項目名です。

「社員が保持するノートPC」、「倉庫の棚に保管されているノートPC」など、文脈は状況によりますが、いちいちこのような文脈を加味した項目名(?)をつけるのは煩雑です。

なので ecuacion-lib-validation では、文脈を無視した項目名と、その文脈を分けて管理しています。

itemNamePath は、その文脈部分を表しています。
「項目名のパス」という意味ですが、「社員が保持する」というのは、laptop の上位に存在する Employee の情報のことです。

すなわち、 propertyPath をたどりながら、そのそれぞれの node に対する項目名を並べたものが「文脈」となることから、itemName の path という表現になっています。

複数階層のList

Employee の上位階層として、部署を表す Dept を追加します。

Dept.java
public record Dept(String name, List<@Valid Employee> employeeList) {

}

こんなコードで実行します。

Main.java(一部抜粋)
  private static void 複数階層のList() {
    List<Laptop> laptopList = List.of(new Laptop(1, null));
    Dept dept = new Dept("Sales",
        List.of(new Employee("John", List.of()), new Employee("Paul", laptopList)));

    try {
      MessageParameters params = ValidationUtil.messageParameters().showsItemNamePath(true);
      ValidationUtil.validateThenThrow(dept, params);

    } catch (ConstraintViolationException ex) {
      for (String message : ExceptionUtil.getMessageList(ex, true)) {
        System.out.println(message);
      }
    }
  }

こちらも記載。

messages.properties または item_names.properties
dept.employeeList=部署所属社員

実行結果はこちら。

実行結果
「部署所属社員」の2番目の要素の中の、「社員保持ノートPC(複数)」の1番目の要素の「機種」は入力必須です。

List<String> などの取り扱い

アカウントに複数メールアドレスがある、と言うパターン。
List の要素に対して @NotNull をつけています。

Account.java
public record Account(String name, @NotNull List<@NotNull String> mailAddresses) {

}

実行用コードはこちら。

Main.java(一部抜粋)
  private static void List_String_の取り扱い() {
    List<String> mailAddressList = Arrays.asList("test@test.com", null);
    Account account = new Account("John", mailAddressList);

    try {
      MessageParameters params = ValidationUtil.messageParameters().showsItemNamePath(true);
      ValidationUtil.validateThenThrow(account, params);

    } catch (ConstraintViolationException ex) {
      for (String message : ExceptionUtil.getMessageList(ex, true)) {
        System.out.println(message);
      }
    }
  }

こちらも記載。

messages.properties または item_names.properties
account.mailAddresses=メールアドレス一覧

実行結果はこちら。

実行結果
「メールアドレス一覧」の2番目の要素は入力必須です。
「メールアドレス一覧」は入力必須です。

サンプルコード

サンプルコードは以下です。
https://github.com/ecuacion-jp/ecuacion-code-snippets/tree/main/ecuacion-lib-core-JakartaValidationList

※このページから直接ソースの zip を download はできないと思うので、そのページにある ecuacion-code-snippets のリンクをクリックし、そこにある緑の <> Code ボタンから Download ZIP で download してください。

本サンプルのフォルダに移動後、mvn compile exec:java で実行できます。

まとめ

Jakarta Validation を使用しメッセージに項目名(フィールド名)含める、ecuacion-lib-validation のご紹介でした。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?