【问题标题】:Force Hibernate to issue DELETEs prior to INSERTs to avoid unique constraint violations?强制 Hibernate 在 INSERT 之前发出 DELETE 以避免违反唯一约束?
【发布时间】:2011-07-13 06:13:33
【问题描述】:

背景: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,并允许用户查看链接到特定债券的所有付款;它允许他们创建新链接,并删除现有链接。一旦他们在页面上完成了所有他们想要的更改,他们点击“保存”,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;

缺点是为监管此约束而创建的索引现在是非唯一索引,因此查询的效率可能会有所降低。我们已经决定这对这个特殊情况没有那么大的损害。另一个缺点(由 Gary 建议)是它可能会受到特定 Oracle 错误的影响——尽管我相信由于应用程序的工作方式,我们将(至少,大部分)免疫。

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

【问题讨论】:

  • 您介意提供一些代码吗,例如:受影响的实体、sf.openSession 和 session.close 之间的代码?
  • 顺便说一下:Hibernate 会在之前插入,但只插入实体,而不插入关系。关系(集合)的插入操作是第 5 步 :-)
  • 我自己不是 Hibernate 编码器 - 我是 Oracle 人 - 我会看看常驻 Java 开发大师能否提供一些细节 :)

标签: oracle hibernate ora-00001 deferrable-constraint


【解决方案1】:

根据您描述的问题,尚不清楚您是否有实体BondPayment,或者您是否有直接链接到PaymentBond。现在,我想你有 PaymentBondBondPayment 之间的链接。在这种情况下,Hibernate 正在做正确的事情,您需要在您的应用程序中添加一些逻辑来检索链接并删除它(或更改它)。像这样的:

bond.getBondPayment().setPayment(newPayment);

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

BondPayment bondPayment = new BondPayment();
bondPayment.setPayment(newPayment);
bondPayment.setBond(bond);
bond.setBondPayment(bondPayment);

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

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

【讨论】:

  • 你是对的,它的Bond - 1:M - BondPayment - M:1 - Payment
  • 因此,您必须按照我提到的方式处理它,这将触发 BondPayments 表中的选择并随后对其进行更新。
  • 嗯,很公平。我不确定他们编写应用程序的方式是否允许这样做。我相信它在页面上都是自动化的,当用户添加 BondPayment 时必须执行一些自定义代码,以搜索用户刚刚删除的旧 BondPayment,并将其更改为更新,听起来比他们做的工作更多准备好做一些“正常工作”的事情:)
  • 已接受。从那以后,我们设置了约束 IMMEDIATE,从而解决了这个问题,因为开发人员已经修改了前端以自动“回收”现有行,而不是删除然后插入它们。
猜你喜欢
  • 1970-01-01
  • 2023-03-30
  • 2016-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-01
  • 2015-08-25
  • 1970-01-01
相关资源
最近更新 更多