Spring数据jpa分离的实体 [英] Spring data jpa detached entity
问题描述
我开始使用Spring Data JPA开发Spring Boot应用程序,以在用户和角色之间建立ManyToMany关系。
这个关系在用户中定义如下class:
@ManyToMany(fetch = FetchType.EAGER,cascade = CascadeType.ALL)
@JoinTable(name = user_role,joinColumns = {@JoinColumn(name =user_id)},inverseJoinColumns = {@JoinColumn(name =role_id)})
private Set< UserRole>角色;
我使用以下方式创建角色:
@Transactional
private void generateSeedRoles(){
UserRole adminRole = new UserRole(RoleEnum.ADMIN.toString());
userRoleRepository.save(adminRole);
UserRole userRole = new UserRole(RoleEnum.USER.toString());
userRoleRepository.save(userRole);
之后向用户分配角色失败:
@Transactional
private void generateSeedUsers(){
UserRole adminRole = userRoleRepository.findUserRoleByRoleName(ADMIN);
User user = User.createUser(user1,user1@user.com,pass);
user.setRoles(new HashSet< UserRole>(Arrays.asList(adminRole)));
userRepository.save(user);
$ / code>
引发以下异常(格式化以提高可读性):
org.springframework.dao.InvalidDataAccessApiUsageException:
传递给persist的分离实体:co.feeb.models.UserRole;
嵌套异常是org.hibernate.PersistentObjectException:
传递给persist的分离实体:co.feeb.models.UserRole
但是,如果在创建关系之前保存了用户,它就可以正常工作:
@Transactional
private void generateSeedUsers(){
UserRole adminRole = userRoleRepository.findUserRoleByRoleName(ADMIN);
User user = User.createUser(user1,user1@user.com,pass);
//保存用户
userRepository.save(user);
//建立关系并更新用户
user.setRoles(new HashSet< UserRole>(Arrays.asList(adminRole)));
userRepository.save(user);
}
不得不保存/更新用户似乎对我来说不合理。有什么方法可以将接收到的角色分配给新用户,而不必先保存用户?
当你保存你的实体时,Spring会在内部检查实体是否是新的(我忘记了确切的机制),并发出持久(如果是新的)或合并(如果存在)。
由于您已将用户定义为UserRole与Cascade.ALL的关系,因此来自用户的任何持续/合并也将级联到UserRole。
鉴于Spring如何实现保存以及上面的级联行为,以下是一些需要考虑的方案:
如果您可以保证您添加到与User的关系中的任何UserRole总是存在,您可以从级联选项中简单地删除Cascade.Persist。否则,我相信在以上两种情况下保存用户时,都必须使用合并 - 通过自定义存储库和entityManager。
关于您使用Cascade的情况。 ALL,在这种情况下可能没有意义,因为你拥有@ManyToMany关系,所以从User到UserRole级联可能会有一些不希望的效果(例如Cascade.Remove每次移除User时都会删除UserRole)。
I started to work on a Spring Boot application using Spring Data JPA to setup a ManyToMany relationship between users and roles.
This relationship is defined as following in the User class:
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinTable(name="user_role", joinColumns = {@JoinColumn(name="user_id")}, inverseJoinColumns = {@JoinColumn(name="role_id")})
private Set<UserRole> roles;
I create roles using:
@Transactional
private void generateSeedRoles() {
UserRole adminRole = new UserRole(RoleEnum.ADMIN.toString());
userRoleRepository.save(adminRole);
UserRole userRole = new UserRole(RoleEnum.USER.toString());
userRoleRepository.save(userRole);
}
Assigning roles to users afterwards fails:
@Transactional
private void generateSeedUsers() {
UserRole adminRole = userRoleRepository.findUserRoleByRoleName("ADMIN");
User user = User.createUser("user1", "user1@user.com", "pass");
user.setRoles(new HashSet<UserRole>(Arrays.asList(adminRole)));
userRepository.save(user);
}
The following exception is thrown (formatted for readability):
org.springframework.dao.InvalidDataAccessApiUsageException:
detached entity passed to persist: co.feeb.models.UserRole;
nested exception is org.hibernate.PersistentObjectException:
detached entity passed to persist: co.feeb.models.UserRole
However, if the user is saved before the relationship is created, it works fine:
@Transactional
private void generateSeedUsers() {
UserRole adminRole = userRoleRepository.findUserRoleByRoleName("ADMIN");
User user = User.createUser("user1", "user1@user.com", "pass");
//Save user
userRepository.save(user);
//Build relationship and update user
user.setRoles(new HashSet<UserRole>(Arrays.asList(adminRole)));
userRepository.save(user);
}
Having to save/update the user twice seems somewhat unreasonable to me. Is there any way to assign the received role to the new user without saving the user first?
When you save your entity, Spring internally checks if the entity is new (I forget the exact mechanism), and issues either a persist (if new) or merge (if existing).
Since you have defined your User to UserRole relationship with Cascade.ALL, any persists/merge from User will cascade to UserRole as well.
Given how Spring implements a save, and the behavior of cascading above, here are some scenarios to consider:
- if both User and UserRole are new - you'll get a persist action when saving User, and a cascading persist to UserRole, which works fine since UserRole is new.
- if User is new but UserRole is existing - again you'll get a persist action when saving User, and a cascading persist to UserRole, which results in an error since UserRole already exists!
If you can guarantee that any UserRole you add to the relationship with User always exists, you could simply remove Cascade.Persist from your cascade options. Otherwise, I believe you'll have to use merge -- via custom repository and entityManager -- when saving your User in both scenarios above.
Regarding your use of Cascade.ALL, which in this situation probably doesn't make sense, since you've got a @ManyToMany relationship, cascading ALL from User to UserRole may have some undesired effects (e.g. Cascade.Remove would remove UserRole every time a User is removed).
这篇关于Spring数据jpa分离的实体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!