You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 14:50:22