“未找到父密钥”尽管它存在(在 TX 内)
我刚刚观察到一个奇怪的行为(当然 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(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:
For your insert problem, you probably have a BEFORE INSERT trigger on the base table (with the
id
colomn filled by a sequence).我明白了。
正如我的问题更新中所述,NHibernate 会话似乎混合了 SQL 语句的顺序。为了解决这个问题,我添加了以下代码行:
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: