复用Hibernate托管对象集合时为何触发唯一约束重复键错误?
Hibernate 6.2.5.Final多对多关联更新问题:复用集合触发唯一约束冲突
我正在使用Hibernate 6.2.5.Final,实体类定义如下:
@Entity @Table(name = "sen_sensor_group") public class SensorGroup implements Serializable { @Id @Column(name = "id") @GenericGenerator( name = "sen_sensor_group_id_seq", strategy = "enhanced-sequence", parameters = { @Parameter(name = "sequence_name", value = "sen_sensor_group_id_seq"), @Parameter(name = "optimizer", value = "pooled-lo"), @Parameter(name = "increment_size", value = "50")}) @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sen_sensor_group_id_seq") private Long id; @ManyToMany(fetch = FetchType.EAGER) @JoinTable( name = "sen_sensor_group_sensor", joinColumns = @JoinColumn(name = "sensor_group_id", nullable = false), inverseJoinColumns = @JoinColumn(name = "sensor_id", nullable = false) ) @OrderBy("name") private Set<Sensor> sensors = new LinkedHashSet<>(); // Other fields... }
更新SensorGroup时,若复用其关联的sensors集合(先清空再重新添加已存在的元素),会触发duplicate key value violates unique constraint错误,看起来Hibernate尝试重新插入已存在的关联记录;但如果创建新集合并替换原集合,更新则正常执行。
两种实现代码
不工作的实现
@Transactional public SensorGroup updateSensorGroup(String username, SensorGroup sensorGroup) { var updatedSensorGroup = getSensorGroup(username, sensorGroup.getSensorGroupId()); updatedSensorGroup.setName(sensorGroup.getName()); // 清空集合后重新添加元素,若添加的是之前已存在的元素会触发约束冲突 updatedSensorGroup.getSensors().clear(); for (var sensor :sensorGroup.getSensors()) { var fetchedSensor = getSensor(username, sensor.getSensorId()); updatedSensorGroup.getSensors().add(fetchedSensor); } return updatedSensorGroup; }
工作的实现
@Transactional public SensorGroup updateSensorGroup(String username, SensorGroup sensorGroup) { var updatedSensorGroup = getSensorGroup(username, sensorGroup.getSensorGroupId()); updatedSensorGroup.setName(sensorGroup.getName()); // 创建新集合替换原集合可正常执行 Set<Sensor> sensorsToKeep = new HashSet<>(); for (var sensor : sensorGroup.getSensors()) { var fetchedSensor = getSensor(username, sensor.getSensorId()); sensorsToKeep.add(fetchedSensor); } updatedSensorGroup.setSensors(sensorsToKeep); return updatedSensorGroup; }
问题原因
这是Hibernate集合跟踪与脏检查机制的特性导致的:
- 当调用
clear()清空集合时,Hibernate会标记该集合需要删除所有关联记录,计划在事务提交时删除关联表sen_sensor_group_sensor中当前SensorGroup的所有条目。 - 后续调用
add()添加元素时,Hibernate会将这些元素标记为需要新增关联,计划插入新的关联记录。 - Hibernate会在事务结束前批量执行这些操作,而插入操作的执行顺序可能早于删除操作——此时旧的关联记录还未被删除,插入重复的
sensor_group_id + sensor_id对就会触发唯一约束冲突。
而替换集合的方式则不同:
- 直接替换集合实例时,Hibernate会将新集合视为全新状态,它会对比新旧集合的差异,只执行增量更新:删除不在新集合中的旧关联,插入新加入的关联,不会全量删除再重新插入,因此不会出现重复键问题。
另外,由于你使用了FetchType.EAGER,集合在加载时就被初始化,Hibernate会全程跟踪这个集合实例的操作;而替换集合相当于让Hibernate放弃对旧集合的跟踪,基于新集合内容做精准的关联更新。
内容的提问来源于stack exchange,提问作者Jeremy Raymond
相关产品推荐
相关产品推荐

