I have a ManyToMany relationship between User and Role. I have a custom hibernate validation constraint on my roles Set in User.
In a @PostConstruct
I save the initial roles (ADMIN, USER) to the database using standard JpaRepository from spring-data-jpa. I then create an initial user using the admin role.
If I do not have my custom validation, the association is saved correctly and I see an entry in user_role
join table. If I have the validation, the user is inserted into the user table, but without an entry into user_role
table. The returned entity has the role in the roles set, but it is not saved into the DB. The code is summarized below. I cannot understand how using the RoleRepo to fetch all of the roles could in any way break the save, but it does.
class User {
@Id
String username;
@ValidOption
@ManyToMany(cascade = {CascadeType.ALL //for example}, fetch=FetchType.EAGER)
Set<Role> roles;
}
class Role {
@Id
String name;
}
class CustomValidator implements ConstraintValidator<ValidOption, Object> {
RoleRepository roleRepo; //injected by spring... have spring factory
@Override
public boolean isValid(Object value, ConstraintValidatorContext context){
roleRepo.findAll() //<-------------- THIS CALL BREAKS THE SAVE
return true;
}
}
@Component
class UserCreator {
RoleRepository roleRepo;
UserRepo userRepo;
@PostConstruct
void setup(){
Role admin = roleRepo.saveAndFlush(new Role('ADMIN'));
roleRepo.saveAndFlush(new Role('USER'));
User user = new User('admin', Collections.singleton(admin));
userRepo.save(user); //<------ DOES NOT INSERT ADMIN INTO USER_ROLE JOIN TABLE
}
}
This works 100% exactly the way I would expect if I remove the custom validator. It may also work if I don't run this in PostConstruct and schedule it in a different thread, I need to check that.
Project with reproducible failing test case: https://github.com/tjhelmuth/SPR-22533/blob/master/src/test/java/spr22533/bug/BugExample.java
Accessing the
EntityManager
during validation is not guaranteed to work during validation.Validation happens in "lifecycle callback methods". For these the following restriction applies (Java Persistence Specification 2.2; Section 3.5.2 Lifecycle Callback Methods):
To make it work, use a separate
EntityManager
, which of course might suffer from seeing a different set of changes since it runs a different transaction.See also: Correct way to do an EntityManager query during Hibernate Validation