bean validationとは、springで有効性検証のための標準、アノテーションベースの制約条件で簡単に検証できます。
bean validationが提供するアノテーションを使って日付データを除去してみようと思います。
validationを適用していない挙動
テストコード、givenPersonのsetBirthdayに存在しない日付をセットするようにして挙動を確認する。@Test
void findByBirthdayBetween() {
givenPerson("martin",10,"A",LocalDate.of(1991,2,21));
givenPerson("david",9,"B",LocalDate.of(1992,7,1));
givenPerson("dennis",8,"O",LocalDate.of(1993,1,5));
givenPerson("sophia",7,"AB",LocalDate.of(1994,6,30));
givenPerson("benny",6,"A",LocalDate.of(1995,8,30));
List<Person> result = personRepository.findByMonthOfBirthday(14);
result.forEach(System.out::println);
}
private void givenPerson(String name, int age, String bloodType, LocalDate birthday) {
Person person = new Person(name, age,bloodType);
person.setBirthday(new Birthday(birthday.getYear(), 14, 35));
personRepository.save(person);
}
package com.informanaging.project.demo.domain.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Embeddable;
import java.time.LocalDate;
@Embeddable
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Birthday {
private int yearOfBirthday;
private int monthOfBirthday;
private int dayOfBirthday;
}
package com.informanaging.project.demo.domain;
import com.informanaging.project.demo.domain.dto.Birthday;
import lombok.*;
import javax.persistence.*;
import javax.validation.Valid;
import java.time.LocalDate;
@Entity
@NoArgsConstructor
@AllArgsConstructor
@RequiredArgsConstructor
@Data
public class Person {
// ・・・省略
@Embedded
private Birthday birthday;
}
結果が以下。ユーザーから存在しない日付を入力してもらってもエラーにならず、動いてしまいます。
validationを適用する
Springでvalidationを使うためには、dependencyを追加する必要があります。
implementation 'org.springframework.boot:spring-boot-starter-validation'
日付の月データは1~12の間の値を持てるし、日データは1~30の間の値を持てますので、その定義をまず
やります。この場合に使えるvalidationのannotationはminとmaxがあります。
以下のように@min(数値) @max(数値)のように使うことができます。
また、エンティティクラスにはbirthdayについて@Validを定義する必要があります。
package com.informanaging.project.demo.domain.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Embeddable;
import java.time.LocalDate;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
@Embeddable
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Birthday {
@Min(1)
@Max(12)
private int monthOfBirthday;
@Min(1)
@Max(31)
private int dayOfBirthday;
private int dayOfBirthday;
}
package com.informanaging.project.demo.domain;
import com.informanaging.project.demo.domain.dto.Birthday;
import lombok.*;
import javax.persistence.*;
import javax.validation.Valid;
import java.time.LocalDate;
import javax.validation.Valid;
@Entity
@NoArgsConstructor
@AllArgsConstructor
@RequiredArgsConstructor
@Data
public class Person {
// ・・・省略
@Valid
@Embedded
private Birthday birthday;
}
修正後のテスト結果。
しかしこれだけだと、2月30日のデータも正しいデータだとプログラムは認識してしまうので、
追加修正が必要です。
BirthdayDTOクラスにBirthday constructorを設けて、受け取るBirthdayデータをLocalDataにてラッピングします。
LocalDateをラッピングすると、渡されるデータが実際に存在しないデータだとエラーを吐き出します。
そのため、実はMinとMaxの対応も要らなくなります。
package com.informanaging.project.demo.domain.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Embeddable;
import java.time.LocalDate;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
@Embeddable
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Birthday {
private int yearOfBirthday;
@Min(1)
@Max(12)
private int monthOfBirthday;
@Min(1)
@Max(31)
private int dayOfBirthday;
public Birthday(LocalDate birthday) {
this.yearOfBirthday = birthday.getYear();
this.monthOfBirthday = birthday.getMonthValue();
this.dayOfBirthday = birthday.getDayOfMonth();
}
}
private void givenPerson(String name, int age, String bloodType, LocalDate birthday) {
Person person = new Person(name, age,bloodType);
person.setBirthday(new Birthday(birthday));
personRepository.save(person);
}