首页 > 解决方案 > CascadeType.MERGE 实际上做了什么?

问题描述

我得到了UserDetails实体表单数据库并将其作为模型属性(MVC)传递。后来,我尝试更新UserDetails数据库中的更新实体。现在我收到了这个错误。 我的疑问是

  1. 的用例是什么CascadeType.MERGE
  2. Hibernate 如何处理实体
  3. 如何更新与另一个实体关联的现有实体。

同一实体 [com.rajath.instagram.entity.UserDetails#1] 的多个表示正在合并。托管:[com.rajath.instagram.entity.UserDetails@637bce04];分离:[com.rajath.instagram.entity.UserDetails@2b6247fa];嵌套异常是 java.lang.IllegalStateException:正在合并同一实体 [com.rajath.instagram.entity.UserDetails#1] 的多个表示。托管:[com.rajath.instagram.entity.UserDetails@637bce04];已分离:[com.rajath.instagram.entity.UserDetails@2b6247fa]],根本原因是 java.lang.IllegalStateException:正在合并同一实体 [com.rajath.instagram.entity.UserDetails#1] 的多个表示。托管:[com.rajath.instagram.entity.UserDetails@637bce04];分离:[com.rajath.instagram.entity.UserDetails@2b6247fa]

控制器层:

@PostMapping("/addUserDetails")
public String adduserDetails(@ModelAttribute("user") UserDetails userDetails) {
   User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
   regisrationService.updateUserDetails(user.getUsername(), userDetails);
   return "redirect:/register/addProfilePhoto";
}

服务层:

@Override
@Transactional
public void updateUserDetails(String username, UserDetails userDetails) {
    
    Optional<InstagramUser> opt = instagramUserJpaDao.findById(username);
    InstagramUser user = opt.get();

    userDetails.setUser(user);
    userDetailsJpaDao.saveAndFlush(userDetails);
}

道层:

@Override
public void updateUserDetails(String username, UserDetails userDetails) {

    Session session = manager.unwrap(Session.class);
    InstagramUser user = session.get(InstagramUser.class, username);
    userDetails.setUser(user);
        
    user.setUserDetails(userDetails);
    session.saveOrUpdate(userDetails);
}

我的实体是:

  1. Instagram用户
// From user details table
@OneToOne(mappedBy="user", fetch=FetchType.LAZY,            cascade=CascadeType.ALL)
private UserDetails userDetails;
  1. 用户详情
    // From user table, @OneToOne matching because, only one user details entry for a user
@OneToOne(fetch=FetchType.EAGER,
          cascade= {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
@JoinColumn(name="username")
private InstagramUser user;

标签: spring-boothibernatejpa

解决方案


您的代码的问题可能InstagramUser.userDetails不是真的懒惰,因此当前UserDetails也被加载到上下文中,因此出现错误。尝试添加optional = false到映射以查看问题是否消失。(请参阅我回答的最后一段,了解为什么这不是正确的解决方案)

或者,您可以尝试通过以下方式重新排序代码:

UserDetails merged = userDetailsJpaDao.save(userDetails); // I am assuming userDetails.user is null at this point
instagramUserJpaDao.findById(username)
    .ifPresent(instagramUser -> merged.setUser(instagramUser));

另请注意,由于在您的原始代码InstagramUser.user中是关联的反面,因此该行user.setUserDetails(userDetails);实际上没有做任何事情。您需要填充UserDetails.user以在两个实体之间建立关联。


推荐阅读