Oracle 强制Hibernate在插入之前发出删除以避免违反唯一约束?

Oracle 强制Hibernate在插入之前发出删除以避免违反唯一约束?,oracle,hibernate,ora-00001,deferrable-constraint,Oracle,Hibernate,Ora 00001,Deferrable Constraint,背景: 我们的桌子是: BOND_PAYMENTS (BOND_PAYMENT_ID, BOND_NUMBER, PAYMENT_ID) 对BOND_PAYMENT_ID有主键约束,对(BOND_编号、PAYMENT_ID)有唯一约束 该应用程序使用Hibernate,允许用户查看与特定债券相关的所有付款;它允许他们创建新链接,并删除现有链接。一旦他们在页面上做了所有想要的更改,他们点击“保存”,Hibernate就会神奇地在数据库上运行所需的SQL。显然,Hibernate计算出哪些记录需要

背景:

我们的桌子是:

BOND_PAYMENTS (BOND_PAYMENT_ID, BOND_NUMBER, PAYMENT_ID)
对BOND_PAYMENT_ID有主键约束,对(BOND_编号、PAYMENT_ID)有唯一约束

该应用程序使用Hibernate,允许用户查看与特定债券相关的所有付款;它允许他们创建新链接,并删除现有链接。一旦他们在页面上做了所有想要的更改,他们点击“保存”,Hibernate就会神奇地在数据库上运行所需的SQL。显然,Hibernate计算出哪些记录需要删除,哪些记录需要插入,其余的记录保持不变。不幸的是,它先进行插入,然后进行删除

如果用户删除了一个指向付款的链接,然后改变主意,重新插入指向同一付款的链接,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错误——尽管我相信,由于应用程序的工作方式,我们将免疫(至少,大部分免疫)


    是否有其他的选择要考虑?

    < P>从您所描述的问题,不清楚您是否有实体“代码>债券支付<代码>,或者如果您有一个<代码>债券直接链接到<代码>付款< /代码>。现在,我假设您通过
    BondPayment
    Payment
    Bond
    之间建立了链接。在这种情况下,Hibernate做的是正确的,您需要在应用程序中添加一些逻辑来检索链接并将其删除(或更改)。大概是这样的:

    bond.getBondPayment().setPayment(newPayment);
    
    BondPayment bondPayment = new BondPayment();
    bondPayment.setPayment(newPayment);
    bondPayment.setBond(bond);
    bond.setBondPayment(bondPayment);
    
    您可能正在做这样的事情:

    bond.getBondPayment().setPayment(newPayment);
    
    BondPayment bondPayment = new BondPayment();
    bondPayment.setPayment(newPayment);
    bondPayment.setBond(bond);
    bond.setBondPayment(bondPayment);
    
    在第一种情况下,会保留
    BondPayment.id
    ,您只需为其更改
    付款。在第二种情况下,它是一个全新的
    BondPayment
    ,它将与数据库中的现有记录冲突


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

    您是否介意提供一些代码,比如:受影响的实体,sf.openSession和session.close之间的代码?顺便说一下:Hibernate以前做过插入,但只为实体插入,不为关系插入。关系(集合)的插入操作是第五步:-)我自己不是Hibernate编码员-我是Oracle的家伙-我会看看常驻Java开发大师能否提供一些细节:)你说得对,它的
    Bond
    -1:m-
    BondPayment
    -m:1-
    Payment
    。因此,你必须按照我提到的方式处理它,这将触发BondPayments表中的一个选择,并对其进行后续更新。我不确定他们编写应用程序的方式是否允许这样做。我相信这一切都是在页面上自动完成的,当用户添加BondPayment时,必须执行一些自定义代码,搜索用户刚刚删除的旧BondPayment,并将其更改为更新,这听起来似乎比他们准备为“正常工作”的东西做的工作要多:)接受。我们已经使约束立即生效,从而解决了这个问题,因为开发人员已经修改了前端以自动“回收”现有行,而不是删除然后插入它们。