Hibernate @OneToMany关联保存时内存集合出现重复元素问题
Hibernate @OneToMany集合内存中出现重复元素但数据库仅一条记录的问题
环境
- Spring Boot 2.6.6
- Spring Data JPA
- Hibernate 5.6.7.Final
实体类定义
@Entity @Table(name = "user") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @OneToMany(cascade = CascadeType.ALL,mappedBy = "user") private List<ContactInfo> contactInfos = new ArrayList<>(); // 省略getter、setter }
@Entity @Table(name = "contact_info") public class ContactInfo { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String phoneNumber; private String address; @ManyToOne(cascade = CascadeType.ALL) @JoinColumn(name = "uid") private User user; // 省略getter、setter }
业务代码
@Transactional public User saveUser() { // 先保存用户到数据库 User user = new User(); user.setName("kevin"); return userRepository.save(user); } @Transactional public User updateContactInfo(Long id) { // 从数据库查询用户并添加ContactInfo User user = userRepository.findById(id).get(); ContactInfo contactInfo = new ContactInfo(); contactInfo.setUser(user); user.getContactInfos().add(contactInfo); User saveUser = userRepository.save(user); System.out.println(saveUser.getContactInfos().size()); // 输出为2 return user; }
问题现象
已将User保存到数据库,调用updateContactInfo方法为其添加ContactInfo后:
- 数据库仅创建一条ContactInfo记录,符合预期
- 但
saveUser的contactInfos集合包含两个相同元素,集合大小为2,不符合仅存一个元素的预期
原因分析
这是Hibernate 5.6.7.Final版本的已知问题:当对托管态实体调用merge操作(Spring Data JPA的save方法对托管态实体默认执行merge)时,Hibernate内部处理集合的逻辑存在bug,会错误地将新添加的元素重复加入内存中的集合,尽管数据库层面的操作是正确的。
同时,业务代码中对托管态实体额外调用save属于冗余操作,进一步触发了该bug的触发条件。
解决办法
1. 移除冗余的save调用
在@Transactional事务中,通过findById获取的是托管态实体,对实体集合的修改会被Hibernate自动追踪,事务提交时会同步到数据库,无需手动调用userRepository.save(user)。修改后的代码如下:
@Transactional public User updateContactInfo(Long id) { User user = userRepository.findById(id).get(); ContactInfo contactInfo = new ContactInfo(); contactInfo.setUser(user); user.getContactInfos().add(contactInfo); // 移除save调用,事务提交时自动同步 System.out.println(user.getContactInfos().size()); // 输出为1 return user; }
2. 使用Set代替List作为集合类型
利用Set不允许重复元素的特性,即使Hibernate内部试图添加重复元素,Set会自动去重。修改User类的集合定义:
@Entity @Table(name = "user") public class User { // ... 其他字段 @OneToMany(cascade = CascadeType.ALL,mappedBy = "user") private Set<ContactInfo> contactInfos = new HashSet<>(); // 省略getter、setter }
3. 升级Hibernate版本
该问题在Hibernate后续版本(如5.6.x的更新版本或6.x系列)中已被修复,可尝试升级Hibernate版本(需注意与Spring Boot 2.6.6的兼容性)。
内容的提问来源于stack exchange,提问作者Kevin
相关产品推荐
相关产品推荐

