I try to manually validate a graph of entities. I didn't have issues doing it when using Hibernate SessionFactory. Since I switched to Hibernate JPA the nested entities are not validated anymore. Why?
- The Hibernate event-based validation is run with the default group and works at pre-persiste/pre-update/pre-remove phases, but a manual validation doesn't detect validation error in nested entities.
- The whole entity graph is eagerly loaded so I assume the TraversableResolver is not the issue here. Anyway I still declared a custom TraversableResolver that always request the navigation to nested entities.
- If I create a fresh entity graph in a unit test, outside of the persistence context, the validation error is found. Yet, if I detach the parent entity from the persistence context the validation error is still not found.
Any help understanding this issue would be greatly appreciated.
I'm using org.springframework.boot:spring-boot-starter-data-jpa
(Spring Boot 2.1.7.RELEASE), packaged with Hibernate 5.3.10.Final. I'm also using Lombok.
Here is my code. Should the alwaysAnError
field be present on the parent class a validation error will be found. If this field is nested in an @Valid child no error is found.
Parent.java
@Data @Builder @NoArgsConstructor @AllArgsConstructor
@Entity
@Table(name = "parent")
public class Parent {
[...]
@Valid
@Builder.Default
@OneToMany(mappedBy = "file", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
@Fetch(value = FetchMode.SUBSELECT)
private List<@Valid Child> children = new ArrayList<>();
}
Child.java
@Data @Builder @NoArgsConstructor @AllArgsConstructor
@Entity
@Table(name = "child")
public class Child {
[...]
@ManyToOne
@JoinColumn(name = "file_id")
private File file;
@NotNull
@Transient
private String alwaysAnError = null;
}
ValidationService.java
Validator validator = Validation.byDefaultProvider()
.configure()
.traversableResolver(new TraversableResolver() {
@Override
public boolean isReachable(Object traversableObject, Path.Node traversableProperty, Class<?> rootBeanType, Path pathToTraversableObject, ElementType elementType) {
return true;
}
@Override
public boolean isCascadable(Object traversableObject, Path.Node traversableProperty, Class<?> rootBeanType, Path pathToTraversableObject, ElementType elementType) {
return true;
}
})
.buildValidatorFactory()
.getValidator();
Set<ConstraintViolation<Declaration>> constraintViolations = validator.validate(fileInstance, Default.class);
if (!constraintViolations.isEmpty()) {
throw new RuntimeException(constraintViolations);
}
Related questions: