“未找到父密钥”尽管它存在(在 TX 内)

发布于 2024-09-14 15:28:34 字数 2207 浏览 4 评论 0原文

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

我尝试将两行插入到父表和子表中,两者都在同一事务中:

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

Child 表有一个 (ParentID) 引用 Parent.ID 外键约束。

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

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

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

问题 1:Oracle 是否会推迟对视图的插入,导致第二条语句失败?

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

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

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


编辑:更多详细信息根据注释:

  • ID 由 NHibernate 使用 Oracle 序列 () 分配,并且是 INSERT 语句的一部分,如下所示我的例子。我还验证了表行中的结果 ID 与映射对象中保存的 NHibernate ID 相匹配。
  • 视图被定义为连接其他表的某些字段的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();
}

I just observed a strange behaviour (of course Oracle is probably supposed to behave this way, but it didn't fit in my world view yet):

I try to insert two rows into a parent and a child table, both within the same transaction:

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

The Child table has a (ParentID) references Parent.ID foreign key constraint.

On the second statement Oracle fails with the error message "Parent key not found."

If I disable the FK constraint, it works. I have asserted that the ParentID and the Parent.ID match, and I am 100% sure that the first line is executed successfully before the second one. Further, I have tried to commit each statement, which worked fine.

However, as the prefixes in my code example suggest, the first INSERT is actually done on a view of the parent table. The reason is that I use NHibernate and the mapping uses the view in background (which didn't cause any problems until today).

Q1: Could it be that inserting on a view is deferred by Oracle so that the second statement fails?

Q2: How can I remedy this problem best?

  • Do I need to define INSTEAD OF triggers on the views?
  • Can I change a setting on the VIEW definition?
  • Can I change a setting on the FOREIGN KEY definition?
  • (I must not bend the hibernate mapping to the original table: It's a demand to use the views so changes and/or security issues can be hidden behind the views)

Details: C# WinForms Application - NHibernate - Oracle 10.2 - T_Child: Sooner or later I will use a view for that table, too, it's simply not defined yet.


Edit: More Details according to the comments:

  • The ID is assigned by NHibernate using an Oracle sequence (<generator class="sequence">), and is part of the INSERT statement as in my example. I also verified that the resulting ID in the table row matches the one NHibernate saved in the mapped object.
  • The view is defined as a SELECT that JOINS some fields of other tables. However, on insert/update I only change the fields belonging to the main table ("T_PARENT"), and that normally works fine.
  • The current foreign key constraint is not deferrable, but that shouldn't have any effect because the parent statement is executed before the child statement. *)

*) Hmm... let me think: Since I use an NHibernate session for submitting the SQL queries, could it be that NHibernate executes them in a different order than I told it to?

I'll investigate on that. => It seems so, see my own answer.

This is how the actual code looks like:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

听闻余生 2024-09-21 15:28:34

如果视图是 已经可更新。在与其基表有简单关系的视图中插入将表现为在基表中插入 - 考虑:

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 由序列填充的列)。

You don't need to define an INSTEAD OF trigger if the view is already updateable. Inserting in a view that has a simple relation with its base table will behave as inserting in the base table -- consider:

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 

For your insert problem, you probably have a BEFORE INSERT trigger on the base table (with the id colomn filled by a sequence).

喜爱纠缠 2024-09-21 15:28:34

我明白了。

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

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

I've got it.

As stated in the update to my question, it seems that the NHibernate session mixes the order of the SQL statements. To remedy this, I added the following line of code:

session.SaveOrUpdate(parent);
session.Flush();
// (...)
query.ExecuteUpdate();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文