Hibernate Проверка коллекций примитивов

Я хочу иметь возможность сделать что-то вроде:

@Email public List getEmailAddresses() { return this.emailAddresses; } 

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

Есть ли способ сделать это?

Ни JSR-303, ни Hibernate Validator не имеют никаких готовых ограничений, которые могут проверять каждый элемент коллекции.

Одним из возможных решений для решения этой проблемы является создание настраиваемого ограничения @ValidCollection и соответствующей реализации валидатора ValidCollectionValidator .

Для проверки каждого элемента коллекции нам нужен экземпляр Validator внутри ValidCollectionValidator ; и для получения такого экземпляра нам нужна пользовательская реализация ConstraintValidatorFactory .

Посмотрите, нравится ли вам следующее решение …

Просто,

  • скопировать все эти classы java (и импортировать classы relavent);
  • добавьте validation-api, hibenate-validator, slf4j-log4j12 и testng jars на пути к classам;
  • запустить тестовый файл.

ValidCollection

  public @interface ValidCollection { Class elementType(); /* Specify constraints when collection element type is NOT constrained * validator.getConstraintsForClass(elementType).isBeanConstrained(); */ Class[] constraints() default {}; boolean allViolationMessages() default true; String message() default "{ValidCollection.message}"; Class[] groups() default {}; Class[] payload() default {}; } 

ValidCollectionValidator

  public class ValidCollectionValidator implements ConstraintValidator, ValidatorContextAwareConstraintValidator { private static final Logger logger = LoggerFactory.getLogger(ValidCollectionValidator.class); private ValidatorContext validatorContext; private Class elementType; private Class[] constraints; private boolean allViolationMessages; @Override public void setValidatorContext(ValidatorContext validatorContext) { this.validatorContext = validatorContext; } @Override public void initialize(ValidCollection constraintAnnotation) { elementType = constraintAnnotation.elementType(); constraints = constraintAnnotation.constraints(); allViolationMessages = constraintAnnotation.allViolationMessages(); } @Override public boolean isValid(Collection collection, ConstraintValidatorContext context) { boolean valid = true; if(collection == null) { //null collection cannot be validated return false; } Validator validator = validatorContext.getValidator(); boolean beanConstrained = validator.getConstraintsForClass(elementType).isBeanConstrained(); for(Object element : collection) { Set> violations = new HashSet> (); if(beanConstrained) { boolean hasValidCollectionConstraint = hasValidCollectionConstraint(elementType); if(hasValidCollectionConstraint) { // elementType has @ValidCollection constraint violations.addAll(validator.validate(element)); } else { violations.addAll(validator.validate(element)); } } else { for(Class constraint : constraints) { String propertyName = constraint.getSimpleName(); propertyName = Introspector.decapitalize(propertyName); violations.addAll(validator.validateValue(CollectionElementBean.class, propertyName, element)); } } if(!violations.isEmpty()) { valid = false; } if(allViolationMessages) { //TODO improve for(ConstraintViolation violation : violations) { logger.debug(violation.getMessage()); ConstraintViolationBuilder violationBuilder = context.buildConstraintViolationWithTemplate(violation.getMessage()); violationBuilder.addConstraintViolation(); } } } return valid; } private boolean hasValidCollectionConstraint(Class beanType) { BeanDescriptor beanDescriptor = validatorContext.getValidator().getConstraintsForClass(beanType); boolean isBeanConstrained = beanDescriptor.isBeanConstrained(); if(!isBeanConstrained) { return false; } Set> constraintDescriptors = beanDescriptor.getConstraintDescriptors(); for(ConstraintDescriptor constraintDescriptor : constraintDescriptors) { if(constraintDescriptor.getAnnotation().annotationType().getName().equals(ValidCollection.class.getName())) { return true; } } Set propertyDescriptors = beanDescriptor.getConstrainedProperties(); for(PropertyDescriptor propertyDescriptor : propertyDescriptors) { constraintDescriptors = propertyDescriptor.getConstraintDescriptors(); for(ConstraintDescriptor constraintDescriptor : constraintDescriptors) { if(constraintDescriptor.getAnnotation().annotationType().getName().equals(ValidCollection.class.getName())) { return true; } } } return false; } } 

ValidatorContextAwareConstraintValidator

 public interface ValidatorContextAwareConstraintValidator { void setValidatorContext(ValidatorContext validatorContext); } 

CollectionElementBean

  public class CollectionElementBean { /* add more properties on-demand */ private Object notNull; private String notBlank; private String email; protected CollectionElementBean() { } @NotNull public Object getNotNull() { return notNull; } public void setNotNull(Object notNull) { this.notNull = notNull; } @NotBlank public String getNotBlank() { return notBlank; } public void setNotBlank(String notBlank) { this.notBlank = notBlank; } @Email public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } } 

ConstraintValidatorFactoryImpl

 public class ConstraintValidatorFactoryImpl implements ConstraintValidatorFactory { private ValidatorContext validatorContext; public ConstraintValidatorFactoryImpl(ValidatorContext nativeValidator) { this.validatorContext = nativeValidator; } @Override public > T getInstance(Class key) { T instance = null; try { instance = key.newInstance(); } catch (Exception e) { // could not instantiate class e.printStackTrace(); } if(ValidatorContextAwareConstraintValidator.class.isAssignableFrom(key)) { ValidatorContextAwareConstraintValidator validator = (ValidatorContextAwareConstraintValidator) instance; validator.setValidatorContext(validatorContext); } return instance; } } 

Наемный рабочий

 public class Employee { private String firstName; private String lastName; private List emailAddresses; @NotNull public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } @ValidCollection(elementType=String.class, constraints={Email.class}) public List getEmailAddresses() { return emailAddresses; } public void setEmailAddresses(List emailAddresses) { this.emailAddresses = emailAddresses; } } 

команда

 public class Team { private String name; private Set members; public String getName() { return name; } public void setName(String name) { this.name = name; } @ValidCollection(elementType=Employee.class) public Set getMembers() { return members; } public void setMembers(Set members) { this.members = members; } } 

Корзина

 public class ShoppingCart { private List items; @ValidCollection(elementType=String.class, constraints={NotBlank.class}) public List getItems() { return items; } public void setItems(List items) { this.items = items; } } 

ValidCollectionTest

 public class ValidCollectionTest { private static final Logger logger = LoggerFactory.getLogger(ValidCollectionTest.class); private ValidatorFactory validatorFactory; @BeforeClass public void createValidatorFactory() { validatorFactory = Validation.buildDefaultValidatorFactory(); } private Validator getValidator() { ValidatorContext validatorContext = validatorFactory.usingContext(); validatorContext.constraintValidatorFactory(new ConstraintValidatorFactoryImpl(validatorContext)); Validator validator = validatorContext.getValidator(); return validator; } @Test public void beanConstrained() { Employee se = new Employee(); se.setFirstName("Santiago"); se.setLastName("Ennis"); se.setEmailAddresses(new ArrayList ()); se.getEmailAddresses().add("segmail.com"); Employee me = new Employee(); me.setEmailAddresses(new ArrayList ()); me.getEmailAddresses().add("[email protected]"); Team team = new Team(); team.setMembers(new HashSet()); team.getMembers().add(se); team.getMembers().add(me); Validator validator = getValidator(); Set> violations = validator.validate(team); for(ConstraintViolation violation : violations) { logger.info(violation.getMessage()); } } @Test public void beanNotConstrained() { ShoppingCart cart = new ShoppingCart(); cart.setItems(new ArrayList ()); cart.getItems().add("JSR-303 Book"); cart.getItems().add(""); Validator validator = getValidator(); Set> violations = validator.validate(cart, Default.class); for(ConstraintViolation violation : violations) { logger.info(violation.getMessage()); } } } как public class ValidCollectionTest { private static final Logger logger = LoggerFactory.getLogger(ValidCollectionTest.class); private ValidatorFactory validatorFactory; @BeforeClass public void createValidatorFactory() { validatorFactory = Validation.buildDefaultValidatorFactory(); } private Validator getValidator() { ValidatorContext validatorContext = validatorFactory.usingContext(); validatorContext.constraintValidatorFactory(new ConstraintValidatorFactoryImpl(validatorContext)); Validator validator = validatorContext.getValidator(); return validator; } @Test public void beanConstrained() { Employee se = new Employee(); se.setFirstName("Santiago"); se.setLastName("Ennis"); se.setEmailAddresses(new ArrayList ()); se.getEmailAddresses().add("segmail.com"); Employee me = new Employee(); me.setEmailAddresses(new ArrayList ()); me.getEmailAddresses().add("[email protected]"); Team team = new Team(); team.setMembers(new HashSet()); team.getMembers().add(se); team.getMembers().add(me); Validator validator = getValidator(); Set> violations = validator.validate(team); for(ConstraintViolation violation : violations) { logger.info(violation.getMessage()); } } @Test public void beanNotConstrained() { ShoppingCart cart = new ShoppingCart(); cart.setItems(new ArrayList ()); cart.getItems().add("JSR-303 Book"); cart.getItems().add(""); Validator validator = getValidator(); Set> violations = validator.validate(cart, Default.class); for(ConstraintViolation violation : violations) { logger.info(violation.getMessage()); } } } 

Вывод

 02:16:37,581 INFO main validation.ValidCollectionTest:66 - {ValidCollection.message} 02:16:38,303 INFO main validation.ValidCollectionTest:66 - may not be null 02:16:39,092 INFO main validation.ValidCollectionTest:66 - not a well-formed email address 02:17:46,460 INFO main validation.ValidCollectionTest:81 - may not be empty 02:17:47,064 INFO main validation.ValidCollectionTest:81 - {ValidCollection.message} 

Примечание. – Когда bean имеет ограничения, НЕ указывайте атрибут constraints для ограничения @ValidCollection . Атрибут constraints необходим, если bean не имеет ограничений.

Невозможно написать общую аннотацию обертки, такую ​​как @EachElement чтобы обернуть любую аннотацию ограничения – из-за ограничений самих аннотаций Java. Тем не менее, вы можете написать общий class проверки валидатора, который делегирует фактическую проверку каждого элемента на существующий валидатор ограничений. Вы должны написать аннотацию обертки для каждого ограничения, но только один валидатор.

Я реализовал этот подход в jirutka / validator-collection (доступен в Maven Central). Например:

 @EachSize(min = 5, max = 255) List values; 

Эта библиотека позволяет вам легко создать «псевдо ограничение» для любого ограничения проверки, чтобы аннотировать коллекцию простых типов, не записывая дополнительный валидатор или ненужные classы-оболочки для каждой коллекции. Ограничение EachX поддерживается для всех стандартных ограничений проверки Bean и ограничений Hibernate.

Чтобы создать @EachAwesome для вашего собственного ограничения @Awesome , просто скопируйте и вставьте class annotations, замените аннотацию @Constraint(validatedBy = CommonEachValidator.class) и добавьте аннотацию @EachConstraint(validateAs = Awesome.class) . Это все!

 // common boilerplate @Documented @Retention(RUNTIME) @Target({METHOD, FIELD, ANNOTATION_TYPE}) // this is important! @EachConstraint(validateAs = Awesome.class) @Constraint(validatedBy = CommonEachValidator.class) public @interface EachAwesome { // copy&paste all attributes from Awesome annotation here String message() default ""; Class[] groups() default {}; Class[] payload() default {}; String someAttribute(); } 

EDIT: Обновлено для текущей версии библиотеки.

У меня нет достаточно высокой репутации, чтобы прокомментировать это в оригинальном ответе, но, возможно, стоит отметить этот вопрос, что JSR-308 находится на заключительном этапе выпуска и будет решать эту проблему, когда он будет выпущен! Однако, по крайней мере, потребуется Java 8.

Единственное различие заключалось бы в том, что аннотация для проверки будет включена в объявление типа.

 //@Email public List<@Email String> getEmailAddresses() { return this.emailAddresses; } 

Пожалуйста, дайте мне знать, где вы думаете, что я могу лучше всего поместить эту информацию для других, кто смотрит. Благодаря!

PS Для получения дополнительной информации ознакомьтесь с этим сообщением SO .

Спасибо за отличный ответ от becomputer06. Но я думаю, что к определению ValidCollection следует добавить следующие annotations:

 @Target( { ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = ValidCollectionValidator.class) 

И я до сих пор не понимаю, что делать с коллекциями примитивных типов обертки и ограничивает annotations, такие как @Size, @Min, @Max и т. Д., Потому что значение не может быть передано через способ becomputer06.

Конечно, я могу создавать пользовательские annotations для всех случаев в моем приложении, но в любом случае мне нужно добавить свойства для этих аннотаций к CollectionElementBean. И это кажется довольно плохим решением.

JSR-303 имеет возможность расширять целевые типы встроенных ограничений: см. 7.1.2. Переопределение ограничений в XML .

Вы можете реализовать ConstraintValidator> который делает то же самое, что и заданные ответы, делегируя примитивный валидатор. Затем вы можете сохранить свое определение модели и применить @Email в List .

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

Пример:

 public class EmailAddress { @Email String email; public EmailAddress(String email){ this.email = email; } } public class Foo { /* Validation that works */ @Valid List getEmailAddresses(){ return this.emails.stream().map(EmailAddress::new).collect(toList()); } } 
  • Использование '? расширяет 'и'? супер 'в compilationе generics
  • Как инициализировать значения HashSet по построению?
  • Быстрее добавлять в коллекцию, сортировать ее или добавлять в сортированную коллекцию?
  • Легкий способ изменить Iterable в коллекцию
  • Как отсортировать ArrayList, используя несколько критериев сортировки?
  • loop on list with remove
  • Как создать новый список на Java
  • Коллекция и List , что вы должны использовать на своих интерфейсах?
  • Добавить несколько элементов в уже инициализированный arraylist в java
  • Почему мой код Entity Framework Code First proxy null и почему я не могу его установить?
  • Соображения производительности для keySet () и entrySet () карты
  • Interesting Posts

    jquery clone div и добавьте его после определенного div

    В ответ на вопрос, как добавить сообщение об ошибке в сводку проверки?

    Marsmallow: изменение разрешений при запуске приложения

    Ошибка: Status {statusCode = DEVELOPER_ERROR, разрешение = null}

    Как центрировать изображение, если оно больше, чем его контейнер?

    Ярлык в Word или Excel для специальной пасты?

    Как очистить БД в герою

    Что люди считают настолько привлекательными для динамических языков?

    Проблемы с std :: stoi, не работающие на MinGW GCC 4.7.2

    Разница в boto3 между ресурсом, клиентом и сеансом?

    Сколько времени потребуется, чтобы сократить количество разделов на 44 ГБ до 10 ГБ

    Возможно ли воспроизводить streamи интернет-радиосигналов shoutcast с помощью html5?

    Добавить ON DELETE поведение CASCADE в таблицу sqlite3 после ее создания

    Эмулирование правой клавиши Ctrl на клавиатуре MacBook Pro без фактической правой клавиши Ctrl

    Как сравнить две фигуры?

    Давайте будем гением компьютера.