Перейти к основному содержимому

Конфигурация динамической проверки DTO, полученная из базы данных

· 4 мин. чтения

1. Обзор

В этом руководстве мы рассмотрим, как создать пользовательскую аннотацию проверки, которая использует регулярное выражение, полученное из базы данных, для сопоставления со значением поля .

Мы будем использовать Hibernate Validator в качестве базовой реализации.

2. Зависимости Maven

Для разработки нам понадобятся следующие зависимости:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.4.0</version>
</dependency>

Последние версии spring-boot-starter-thymeleaf , spring-boot-starter-data-jpa можно загрузить с Maven Central.

3. Пользовательская аннотация проверки

В нашем примере мы создадим пользовательскую аннотацию с именем @ContactInfo , которая будет проверять значение на соответствие регулярному выражению, полученному из базы данных. Затем мы применим эту проверку к полю contactInfo класса POJO с именем Customer .

Чтобы получить регулярные выражения из базы данных, мы будем моделировать их как класс сущности ContactInfoExpression .

3.1. Модели данных и репозиторий

Создадим класс Customer с полями id и contactInfo :

@Entity
public class Customer {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;

private String contactInfo;

// standard constructor, getters, setters
}

Далее давайте взглянем на класс ContactInfoExpression , который будет хранить значения регулярного выражения в свойстве с именем pattern :

@Entity
public class ContactInfoExpression {

@Id
@Column(name="expression_type")
private String type;

private String pattern;

//standard constructor, getters, setters
}

Затем давайте добавим интерфейс репозитория на основе Spring Data для управления сущностями ContactInfoExpression :

public interface ContactInfoExpressionRepository 
extends Repository<ContactInfoExpression, String> {

Optional<ContactInfoExpression> findById(String id);
}

3.2. Настройка базы данных

Для хранения регулярных выражений мы будем использовать базу данных H2 в памяти со следующей конфигурацией персистентности:

@EnableJpaRepositories("com.foreach.dynamicvalidation.dao")
@EntityScan("com.foreach.dynamicvalidation.model")
@Configuration
public class PersistenceConfig {

@Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase db = builder.setType(EmbeddedDatabaseType.H2)
.addScript("schema-expressions.sql")
.addScript("data-expressions.sql")
.build();
return db;
}
}

Два упомянутых скрипта используются для создания схемы и вставки данных в таблицу contact_info_expression :

CREATE TABLE contact_info_expression(
expression_type varchar(50) not null,
pattern varchar(500) not null,
PRIMARY KEY ( expression_type )
);

Сценарий data-expressions.sql добавит три записи для представления типов электронной почты , телефона и веб- сайта . Они представляют собой регулярные выражения для проверки того, что значение является действительным адресом электронной почты, действительным номером телефона в США или действительным URL-адресом:

insert into contact_info_expression values ('email',
'[a-z0-9!#$%&*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?')
insert into contact_info_expression values ('phone',
'^([0-9]( |-)?)?(\(?[0-9]{3}\)?|[0-9]{3})( |-)?([0-9]{3}( |-)?[0-9]{4}|[a-zA-Z0-9]{7})$')
insert into contact_info_expression values ('website',
'^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$')

3.3. Создание пользовательского валидатора

Давайте создадим класс ContactInfoValidator , содержащий фактическую логику проверки. Следуя рекомендациям спецификации Java Validation, класс реализует интерфейс ConstraintValidator и переопределяет метод isValid() .

Этот класс получит значение используемого в настоящее время типа контактной информации — адрес электронной почты , телефон или веб-сайт — который установлен в свойстве с именем contactInfoType , а затем использует его для извлечения значения регулярного выражения из базы данных:

public class ContactInfoValidator implements ConstraintValidator<ContactInfo, String> {

private static final Logger LOG = Logger.getLogger(ContactInfoValidator.class);

@Value("${contactInfoType}")
private String expressionType;

private String pattern;

@Autowired
private ContactInfoExpressionRepository expressionRepository;

@Override
public void initialize(ContactInfo contactInfo) {
if (StringUtils.isEmptyOrWhitespace(expressionType)) {
LOG.error("Contact info type missing!");
} else {
pattern = expressionRepository.findById(expressionType)
.map(ContactInfoExpression::getPattern).get();
}
}

@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (!StringUtils.isEmptyOrWhitespace(pattern)) {
return Pattern.matches(pattern, value);
}
LOG.error("Contact info pattern missing!");
return false;
}
}

Для свойства contactInfoType в файле application.properties можно задать одно из значений email , phone или Website :

contactInfoType=email

3.4. Создание аннотации пользовательского ограничения

А теперь давайте создадим интерфейс аннотации для нашего пользовательского ограничения:

@Constraint(validatedBy = { ContactInfoValidator.class })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface ContactInfo {
String message() default "Invalid value";

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};
}

3.5. Применение пользовательского ограничения

Наконец, давайте добавим аннотации проверки в поле contactInfo нашего класса Customer :

public class Customer {

// ...
@ContactInfo
@NotNull
private String contactInfo;

// ...
}

4. Контроллер Spring и HTML-форма

Чтобы протестировать нашу аннотацию проверки, мы создадим сопоставление запроса Spring MVC, которое использует аннотацию @Valid для запуска проверки объекта Customer :

@PostMapping("/customer")
public String validateCustomer(@Valid Customer customer, BindingResult result, Model model) {
if (result.hasErrors()) {
model.addAttribute("message", "The information is invalid!");
} else {
model.addAttribute("message", "The information is valid!");
}
return "customer";
}

Объект Customer отправляется контроллеру из HTML-формы:

<form action="customer" method="POST">
Contact Info: <input type="text" name="contactInfo" /> <br />
<input type="submit" value="Submit" />
</form>
<span th:text="${message}"></span>

Чтобы завершить все это, мы можем запустить наше приложение как приложение Spring Boot:

@SpringBootApplication
public class DynamicValidationApp {
public static void main(String[] args) {
SpringApplication.run(DynamicValidationApp.class, args);
}
}

5. Вывод

В этом примере мы показали, как можно создать пользовательскую аннотацию проверки, которая динамически извлекает регулярное выражение из базы данных и использует его для проверки аннотированного поля.

Полный исходный код примера можно найти на GitHub .