当前位置: 首页 > 面试题库 >

Spring Data JPA分离实体

朱自明
2023-03-14
问题内容

我开始使用Spring Data JPA在Spring Boot应用程序上进行工作,以在用户和角色之间建立ManyToMany关系。

此关系在User类中定义如下:

@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;

我使用以下角色创建角色:

@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);
}

引发以下异常(为便于阅读而设置格式):

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

但是,如果在创建关系之前保存了用户,则可以正常工作:

@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);
}

我不得不两次保存/更新用户似乎有些不合理。有什么方法可以在不先保存 用户的 情况下将接收到的角色分配给 新用户


问题答案:

保存实体时,Spring在内部检查该实体是否是新实体(我忘记了确切的机制),并发出持久性(如果是新实体)或合并(如果存在)。

由于您已经定义了Cascade.ALL的User to UserRole关系,因此任何来自User的持久/合并也将级联到UserRole。

鉴于Spring如何实现保存以及上面的级联行为,这里有一些需要考虑的场景:

  • 如果User和UserRole都是新的-保存User时,您将获得持久操作,并且级联将持久保存到UserRole,因为UserRole是新的,因此可以很好地工作。
  • 如果User是新用户,但UserRole存在-在保存User时,您将再次执行持久操作,并且级联将持久保存到UserRole,这将导致错误,因为UserRole已经存在!

如果可以保证添加到与用户的关系中的任何UserRole始终存在,则只需从级联选项中删除Cascade.Persist。否则,我相信在上述两种情况下保存用户时,都必须使用合并-
通过自定义存储库和EntityManager。

关于您使用Cascade.ALL的情况,在这种情况下这可能是没有意义的,因为您有@ManyToMany关系,所以将ALL从User层叠到UserRole可能会产生一些不良后果(例如Cascade.Remove每次都会删除UserRole用户已删除)。



 类似资料:
  • 问题内容: 我有以下代码: 这会在第四行引发IllegalArgumentException,提示“实体未管理”。如果我将第三行改为而不是,那么一切似乎都可以正常工作。 这里发生了什么?我可以防止这种情况发生吗? 更新: @DataNucleus将我引向PersistenceContext。如何在代码中更改持久性上下文? 问题答案: 根据Eval 2.0 Eval的JSR-000317持久性规范:

  • 问题内容: “独立实体”是什么意思?在交易期间如何将托管实体转换为独立实体? 问题答案: 分离实体是状态不能由JPA提供程序反映的实体。 换句话说,如果您更改其状态(即通过setter方法),则这些更改将不会保存到基础数据库中,因为JPA提供程序不必“观察”此类实体。 如果实体E1是受管实体,则可以使其分离调用(非常合理的命名)方法。您还可以使用它将清除整个PersistenceContext并有

  • 我目前正在开发一个具有核心模块和不同“扩展”模块的应用程序。核心模块包括基于Spring配置类的JPA配置以及一些“基本”实体及其存储库,这些实体和存储库将在“扩展”模块中使用。扩展模块包含额外的实体类和JPARepositories。启动扩展模块的JUnit测试时,我遇到以下错误: 为了做到这一点,我尝试了三种不同的方法: 创建了一个名为coreEntityManager和setPackages

  • 我有一个java代码,我正在更新staff实体。我正在使用应用程序管理的实体管理器。我在登录页面中有一个条件,如果用户提供了正确的用户名/密码,他的上次登录时间将被更新。 但是更新没有发生,我得到一个实体已被分离的异常。请参阅下面的代码:- staffDAOImpl.java:- 在StaffServiceImpl.java类中,您将看到我通过调用“StaffDAO.findUnique(logi

  • 问题内容: 我正在使用Spring Boot并通过jpahibernate。我正在使用JpaRepository接口来实现我的存储库。与以下UserRepository一样 我想实现以下目标 加载用户实体。 更改实体对象的状态,例如user.setName(“ foo”) 进行外部系统Web服务调用。将通话结果保存到数据库中 仅在成功响应此Web服务调用后,才将用户的新状态保存在存储库中。 以上所

  • 我正在JPA上使用spring boot和hibernate。我正在使用JpaRepository接口来实现我的存储库。与下面的UserRepository一样 我想实现以下目标 加载用户实体。 更改实体对象的状态,例如user.setname(“foo”) 执行外部系统webservice调用。将调用结果保存在DB中 仅当成功响应此webservice调用时,才将用户的新状态保存在存储库中。 以