«Родительский ключ не найден», хотя он существует (в TX) - PullRequest
2 голосов
/ 17 августа 2010

Я только что заметил странное поведение (конечно, Oracle, вероятно, должен вести себя таким образом, но это еще не вписывается в мое мировоззрение):

Я пытаюсь вставить две строки в родительский идочерняя таблица, обе в одной и той же транзакции:

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?
  • Можно ли изменить настройку в определении FOREIGN KEY?
  • (I не должен сгибает отображение гибернации к исходной таблице: требуется использование представлений, чтобы изменения и / или проблемы безопасности могли быть скрыты за представлениями)

Подробности: C # WinForms Application -NHibernate - Oracle 10.2 - T_Child: Рано или поздно я тоже буду использовать представление для этой таблицы, просто оно еще не определено.


Редактировать : Подробнее по комментариям:

  • ID назначается NHibernate с использованием последовательности Oracle (<generator class="sequence">) и является частью оператора INSERT, как в моем примере.Я также проверил, что полученный идентификатор в строке таблицы соответствует одному NHibernate, сохраненному в сопоставленном объекте.
  • Представление определяется как SELECT, который СОЕДИНЯЕТ некоторые поля других таблиц.Однако при вставке / обновлении я изменяю только поля, принадлежащие основной таблице ("T_PARENT"), и это обычно работает нормально.
  • Текущее ограничение внешний ключ равно , а неdeferrable , но это не должно иметь никакого эффекта, поскольку родительский оператор выполняется перед дочерним оператором.*)

*) Хм ... позвольте мне подумать: поскольку я использую сеанс 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();
}

Ответы [ 2 ]

1 голос
/ 17 августа 2010

Я понял.

Как указано в обновлении моего вопроса, похоже, что сеанс NHibernate смешивает порядок операторов SQL. Чтобы исправить это, я добавил следующую строку кода:

session.SaveOrUpdate(parent);
session.Flush();
// (...)
query.ExecuteUpdate();
1 голос
/ 17 августа 2010

Вам не нужно определять триггер 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, заполненной последовательностью).

...