强制Hibernate在INSERT之前发出DELETE,以避免违反唯一约束?

Jef*_*emp 6 oracle hibernate deferrable-constraint ora-00001

背景:http://jeffkemponoracle.com/2011/03/11/handling-unique-constraint-violations-by-hibernate

我们的表是:

BOND_PAYMENTS (BOND_PAYMENT_ID, BOND_NUMBER, PAYMENT_ID)
Run Code Online (Sandbox Code Playgroud)

BOND_PAYMENT_ID上有主键约束,(BOND_NUMBER,PAYMENT_ID)上有唯一约束.

该应用程序使用Hibernate,并允许用户查看链接到特定Bond的所有付款; 它允许他们创建新链接,并删除现有链接.一旦他们在页面上做了所有他们想要的更改,他们就会点击"保存",Hibernate会在数据库上运行所需的SQL.显然,Hibernate可以找出需要删除的记录,需要插入哪些记录,并保持其余部分不变.不幸的是,它首先执行INSERT,然后执行DELETE.

如果用户删除了付款的链接,然后改变主意并重新插入指向同一付款的链接,Hibernate很乐意尝试插入然后将其删除.由于这些插入/删除作为单独的SQL语句运行,因此Oracle会在第一个插入时立即验证约束并发出违反ORA-00001唯一约束的情况.

我们只知道两种选择:

  1. 使约束可以推迟
  2. 删除唯一约束

选项2不太合适,因为约束提供了极好的保护,可以防止可能允许保存不一致数据的令人讨厌的应用程序错误.我们选择了1.

ALTER TABLE bond_payments ADD
  CONSTRAINT bond_payment_uk UNIQUE (bond_number, payment_id)
  DEFERRABLE INITIALLY DEFERRED;
Run Code Online (Sandbox Code Playgroud)

缺点是为警告此约束而创建的索引现在是一个非唯一索引,因此查询的效率可能稍低.我们已经确定这对于这个特殊情况并没有那么大的损害.另一个缺点(由Gary建议)可能会遇到特定的Oracle错误 - 虽然我相信由于应用程序的工作方式,我们会免疫(至少大部分).

我们还应该考虑其他选择吗?

jpk*_*ing 2

从您描述的问题来看,不清楚您是否有一个实体BondPayment,或者是否有一个Bond直接链接到Payment. 现在,我想您已经有了PaymentBondthrough之间的链接BondPayment。在这种情况下,Hibernate 正在做正确的事情,您需要在应用程序中添加一些逻辑来检索链接并将其删除(或更改它)。像这样的东西:

bond.getBondPayment().setPayment(newPayment);
Run Code Online (Sandbox Code Playgroud)

你可能正在做这样的事情:

BondPayment bondPayment = new BondPayment();
bondPayment.setPayment(newPayment);
bondPayment.setBond(bond);
bond.setBondPayment(bondPayment);
Run Code Online (Sandbox Code Playgroud)

在第一种情况下, 被BondPayment.id保留,而您只需更改它payment。在第二种情况下,它是一个全新的BondPayment,它将与数据库中的现有记录冲突。

我说 Hibernate 正在做正确的事情,因为它BondPayment作为一个“常规”实体受到威胁,其生命周期由您的应用程序定义。User这与在 上具有唯一约束的相同login,并且您试图插入具有重复的第二条记录login。Hibernate 会接受(它不知道login数据库中是否存在),而你的数据库会拒绝。