Hibernate @ManyToMany 关系上的唯一约束冲突

Unique Constraint Violation on Hibernate @ManyToMany Relationship

我的数据库中有 3 个实体,我们称它们为 ABC

AB 彼此共享多对多关系。 ABSortedSet,但 B 没有引用 A(没有集合或配置 w/e)。所以我们有以下内容。

// Inside class A
@ManyToMany
@JoinTable(name = "a_b", 
           joinColumns = {@JoinColumn(name = "a_id")}, 
           inverseJoinColumns = {@JoinColumn(name = "b_id")})
@LazyCollection(LazyCollectionOption.FALSE)
@SortNatural
private SortedSet<B> bSet = new TreeSet<B>();

BC 之间存在一对多的关系(1 B 对许多 C)。 C 在它的实体中有一个 B,但 B 没有对它的许多 C 实体的引用。所以我们有以下

// Inside class C
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "b_id", nullable = false, updatable = true)
protected B b;

我们有一个同步进程,每晚运行一次作业,将 A 个实体及其关联更新为 B 个实体(不会经常更改)。我们最终得到类似于以下内容的结果(实际上,DAO、服务等要复杂得多)。

// Get the A value to be updated
A aToUpdate = entityManager.find(A.class, idForA); 

// Out of scope of the question, but we need to figure out B via a string field on C
C cValue = myDao.getByProperty("fieldName", fieldValue);

// Determine the B values to set on aToUpdate
B bToSetOnA = cValue.getB();
TreeSet<B> bSet = new TreeSet<>();
bSet.add(bToSetOnA);

// Update aToUpdate
aToUpdate.setBSet(bSet);
aToUpdate = entityManager.merge(aToUpdate);
entityManager.flush();

发生这种情况时,会出现以下错误。

ERROR [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] (Sync Thread)
Duplicate entry 'myAId-almostMyBId' for key 'uk_a_b'

一件有趣的事是 almostMyBId 比实际的 B ID 少 1 个字符。但只有完整的 ID 出现在 a_b table.

当我查看代码库时,a_b table 上的索引有一个 uk_a_b 约束。这是来自liquibase。

<createIndex indexName="uk_a_b" tableName="a_b" unique="true">
    <column name="a_id"/>
    <column name="b_id"/>
</createIndex>

如果删除 aToUpdate.setBSet(bSet); 行,错误就会消失。

我添加了日志记录并确认新 bSet 的 ID 与 aToUpdate 上的旧 ID 相同。

不知何故,Hibernate 似乎正在尝试重新添加关联,即使我们正在进行合并并且关联并没有真正改变。

我试过在这里和那里更改一些 CascadeType 和 FetchType 东西,但错误似乎并没有消失。有人知道发生了什么事吗?

这非常适合我的情况,但我想无论如何我都会 post 答案,以防有人阅读我的问题并一直在努力。

我团队中的另一位开发人员的任务是根据 aa_b table 加速报告。为避免需要执行 JOIN 和 WHERE 子句,开发人员将数据从 a_b 复制到新的 table 中(使用 WHERE 子句)并添加触发器,以便无论何时更新内容,它都会插入新的 table。这个新的 table 有一个与 a_b table 同名的约束(即 uk_a_b)。重复的案例没有得到妥善处理,因此引发了错误。由于名称相似,它似乎是 a_b table 引起的问题,而实际上它是新的 table。娱乐时间。