【问题标题】:"Parent Key Not Found" although it exists (within the TX)“未找到父密钥”虽然存在(在 TX 内)
【发布时间】:2010-08-17 08:03:02
【问题描述】:

我刚刚观察到一个奇怪的行为(当然甲骨文可能应该这样做,但它还不符合我的世界观):

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

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

子表有一个(ParentID) references Parent.ID 外键约束。

在第二条语句中,Oracle 失败并显示错误消息 “未找到父键。”

如果我禁用 FK 约束,它会起作用。我已经断言 ParentID 和 Parent.ID 匹配,并且我 100% 确定第一行在第二行之前成功执行。此外,我尝试提交每条语句,效果很好。

然而, 正如我的代码示例中的前缀所暗示的,第一个 INSERT 实际上是在父表的视图 上完成的。原因是我使用 NHibernate 并且映射使用背景中的视图(直到今天才引起任何问题)。

Q1:会不会是 Oracle 推迟了在视图上的插入操作,导致第二条语句失败?

Q2:我怎样才能最好地解决这个问题?

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

详细信息:C# WinForms 应用程序 - NHibernate - Oracle 10.2 - T_Child:迟早我也会为该表使用视图,它只是尚未定义。


编辑:根据 cmets 的更多详细信息:

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

*) 嗯...让我想想:既然我使用 NHibernate 会话来提交 SQL 查询,会不会是 NHibernate 以不同于我告诉它的顺序执行它们?

我会对此进行调查。 => 看来是这样,请参阅我自己的答案。

这是实际代码的样子:

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();
}

【问题讨论】:

  • 子表可以先插入吗?你能检查一下吗?还可以查看可延迟约束(SET CONSTRAINT ...),它允许您以任何顺序插入数据并仅在事务结束时验证数据download.oracle.com/docs/cd/B10500_01/appdev.920/a96590/…
  • 视图的定义是什么?它是否已经有一个 INSTEAD OF 触发器?
  • 也许有一个触发器将您的 ID 777 替换为从序列中新生成的数字?
  • 大家好,ID 是 NHibernate 从 oracle 序列 (<generator class="sequence">) 生成的,并包含在 INSERT 语句中。数据库不会更改 ID(我验证了我可以在数据库中找到的 ID 与映射对象中找到的 ID 匹配。
  • 数据库中没有触发器

标签: sql oracle oracle10g views foreign-keys


【解决方案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 列由序列填充)。

【讨论】:

  • 嗨文森特,我不知道。 ID 由 NHibernate 使用 Oracle 序列 () 生成,并传递给 INSERT 语句。该视图确实是可更新的,这些语句确实插入了数据,我通过单独提交每个语句来验证这一点。只有当我在两者之间不提交时,它才会起作用。
【解决方案2】:

我知道了。

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

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

【讨论】:

    猜你喜欢
    • 2020-07-23
    • 2012-03-28
    • 2016-10-08
    • 1970-01-01
    • 1970-01-01
    • 2014-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多