背景:http://jeffkemponoracle.com/2011/03/11/handling-unique-constraint-violations-by-hibernate
我们的表是:
BOND_PAYMENTS (BOND_PAYMENT_ID, BOND_NUMBER, PAYMENT_ID)
BOND_PAYMENT_ID上有主键约束,(BOND_NUMBER,PAYMENT_ID)有唯一约束。
该应用程序使用Hibernate,并允许用户查看链接到特定Bond的所有付款;它允许他们创建新链接,并删除现有链接。一旦他们在页面上做了所有他们想要的更改,他们就会点击“保存”,Hibernate会在数据库上运行所需的SQL。显然,Hibernate可以找出需要删除的记录,需要插入哪些记录,并保持其余部分不变。不幸的是,它首先执行INSERT,然后执行DELETE。
如果用户删除了付款的链接,然后改变主意并重新插入同一付款的链接,Hibernate很乐意尝试插入然后删除它。由于这些插入/删除作为单独的SQL语句运行,因此Oracle会在第一次插入时立即验证约束,并发出违反ORA-00001唯一约束。
我们只知道两个选项:
选项2不太合适,因为约束提供了极好的保护,可以防止可能允许保存不一致数据的令人讨厌的应用程序错误。我们选择了1。
ALTER TABLE bond_payments ADD
CONSTRAINT bond_payment_uk UNIQUE (bond_number, payment_id)
DEFERRABLE INITIALLY DEFERRED;
缺点是为警告此约束而创建的索引现在是一个非唯一索引,因此查询的效率可能稍低。我们已经确定这对于这个特殊情况并没有那么大的损害。另一个缺点(由Gary建议)可能会遇到特定的Oracle错误 - 尽管我认为由于应用程序的运行方式,我们将免疫(至少大部分)。
我们应该考虑其他选择吗?
答案 0 :(得分:2)
根据您描述的问题,目前尚不清楚您是否拥有实体BondPayment
,或者您是否Bond
直接链接到Payment
。目前,我认为您在Payment
和Bond
到BondPayment
之间有链接。在这种情况下,Hibernate正在做正确的事情,你需要在你的应用程序中添加一些逻辑来检索链接并将其删除(或更改它)。像这样:
bond.getBondPayment().setPayment(newPayment);
你可能正在做这样的事情:
BondPayment bondPayment = new BondPayment();
bondPayment.setPayment(newPayment);
bondPayment.setBond(bond);
bond.setBondPayment(bondPayment);
在第一种情况下,会保留BondPayment.id
,您只是更改了payment
。在第二种情况下,它是一个全新的BondPayment
,它将与数据库中的现有记录冲突。
我说Hibernate正在做正确的事情,因为它威胁BondPayment
作为“常规”实体,其生命周期由您的应用定义。这与在User
上具有唯一约束的login
相同,并且您尝试插入带有重复login
的第二条记录。 Hibernate将接受(它不知道数据库中是否存在login
)并且您的数据库将拒绝。