Spring数据jpa分离的实体 [英] Spring data jpa detached entity

查看:137
本文介绍了Spring数据jpa分离的实体的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我开始使用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如何实现保存以及上面的级联行为,以下是一些需要考虑的方案:


  • 如果用户和用户角色都是新的 - 保存用户时会得到一个持久行为,并且级联会持续到UserRole,自UserRole是新角色以后,它将正常工作。

  • 如果User是新的但UserRole已存在 - 再次保存User时会得到一个持久行为,并且级联会持续到UserRole,这会导致错误,因为UserRole已经存在!



如果您可以保证您添加到与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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆