我在User和Role之间有一个ManyToMany关系。我在我的角色Set in User中有一个自定义的hibernate验证约束。
在@PostConstruct
中,我使用spring-data-jpa中的标准JpaRepository将初始角色(ADMIN,USER)保存到数据库。然后,我使用admin角色创建初始用户。
如果我没有自定义验证,则正确保存关联,并在user_role
连接表中看到一个条目。如果我有验证,则会将用户插入到用户表中,但不会输入user_role
表。返回的实体在角色集中具有角色,但不会保存到DB中。代码总结如下。我无法理解如何使用RoleRepo获取所有角色可以以任何方式破坏保存,但确实如此。
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
}
}
如果我删除自定义验证器,这完全符合我的预期。如果我不在PostConstruct中运行它并在不同的线程中安排它,它也可以工作,我需要检查它。
具有可重现的失败测试用例的项目:https://github.com/tjhelmuth/SPR-22533/blob/master/src/test/java/spr22533/bug/BugExample.java
在验证期间访问EntityManager
不能保证在验证期间有效。
验证发生在“生命周期回调方法”中。对于这些限制适用(Java持久性规范2.2;第3.5.2节生命周期回调方法):
通常,可移植应用程序的生命周期方法不应调用EntityManager或查询操作,访问其他实体实例或修改同一持久性上下文中的关系。生命周期回调方法可以修改调用它的实体的非关系状态。
为了使它工作,使用一个单独的EntityManager
,当然可能会因为它运行不同的事务而看到一组不同的更改。
另见:Correct way to do an EntityManager query during Hibernate Validation