“未找到父密钥”,但它存在(在TX内)

时间:2010-08-17 08:03:02

标签: sql oracle oracle10g views foreign-keys

我刚刚观察到一种奇怪的行为(当然Oracle可能会以这种方式表现,但它还不适合我的世界观):

我尝试在同一事务中的父表和子表中插入两行:

INSERT INTO V_Parent (ID, Name) VALUES (777, 'Hello World');
INSERT INTO T_Child (ParentID, Name) VALUES (777, 'Foo Bar');

Child表具有(ParentID) references Parent.ID外键约束。

在第二个声明中,Oracle失败并显示错误消息“未找到父键。”

如果我禁用FK约束,它会起作用。我断言ParentID和Parent.ID匹配,我100%确定第一行在第二行之前成功执行。此外,我试图提交每个声明,这很好。

但是,作为我的代码示例中的前缀建议,第一个 INSERT实际上是在父表的视图上完成的。原因是我使用NHibernate并且映射使用后台视图(直到今天才引起任何问题)。

Q1:是否可以延迟在视图上插入,以便第二个语句失败?

Q2:我如何才能最好地解决这个问题?

  • 我是否需要在视图上定义INSTEAD OF触发器?
  • 我可以更改VIEW定义的设置吗?
  • 我可以更改FOREIGN KEY定义的设置吗?
  • (我不得将hibernate映射弯曲到原始表:这是使用视图的要求,因此更改和/或安全问题可以隐藏在视图后面)

详细信息:C#WinForms应用程序 - NHibernate - Oracle 10.2 - T_Child:我迟早会使用该表的视图,它还没有定义。


修改:根据评论更多详情:

  • ID由NHibernate 使用Oracle序列(<generator class="sequence">)分配,并且是我示例中INSERT语句的一部分。我还验证了表格行中的结果ID与映射对象中保存的NHibernate匹配。
  • 视图被定义为加入其他表的某些字段的SELECT。但是,在插入/更新时,我只更改属于主表的字段(“T_PARENT”),这通常可以正常工作。
  • 当前外键约束是不可延迟,但这不应该有任何效果,因为父语句在子语句之前执行。 *)

*)嗯......让我想一想:由于我使用NHibernate会话来提交SQL查询,可能是NHibernate以不同于我告诉它的顺序执行它们吗? < / p>

我会对此进行调查。 =&gt;看来是这样,看看我自己的答案。

这是实际代码的样子:

ISession session = this.DAOFactory.NHibernateHelper.SessionFactory.OpenSession();
ITransaction tx = session.BeginTransaction();

try
{
    // parent.ID == 0
    session.SaveOrUpdate(parent);
    // parent.ID == 777 (for example)

    ISQLQuery query = session.CreateSQLQuery(
        "INSERT INTO T_CHILD (PARENT_ID, NAME) VALUES (:parentId, :name)");
    query.SetDecimal("parentId", parent.ID);
    query.SetDecimal("name", "Foo Bar");

    query.ExecuteUpdate(); // Fails with ORA-Exception

    tx.Commit();
}
catch (Exception)
{
    tx.Rollback();
    throw;
}
finally
{
    session.Close();
}

2 个答案:

答案 0 :(得分:1)

如果视图为already updateable,则无需定义INSTEAD OF触发器。在与基表具有简单关系的视图中插入将表现为插入基表 - 考虑:

SQL> CREATE TABLE t (a NUMBER, b NUMBER, c NUMBER);

Table created

SQL> CREATE VIEW v AS SELECT a, b FROM t;

View created

SQL> INSERT INTO v VALUES (1,2);

1 row inserted

SQL> SELECT * FROM t;

         A          B          C
---------- ---------- ----------
         1          2 

对于插入问题,您可能在基表上有一个BEFORE INSERT触发器(序列填充了id colomn)。

答案 1 :(得分:1)

我知道了。

正如我的问题更新中所述,似乎NHibernate会话混合了SQL语句的顺序。为了解决这个问题,我添加了以下代码行:

session.SaveOrUpdate(parent);
session.Flush();
// (...)
query.ExecuteUpdate();