Hibernate StatelessSession и странное поведение на Oracle и SQL Server

1

Я использую Hibernate StatelessSession для вставки и обновления строк навалом. Не уверен, что jdbc.batch _size связан с вопросом, но в любом случае в моем приложении я вообще не настраиваю параметр hibernate.jdbc.batch_size.

Я поставлю на сеанс объект соединения, который уже привязан к потоку к существующей транзакции (управляется Spring). Это делается с помощью Spring DataSourceUtils. По этой причине я не закрываю сессию без сохранения состояния, когда я закончил обработку данных, потому что мне все еще нужно открыть соединение для нескольких других вещей, пока транзакция не будет завершена.

В основном, что-то вроде этого:

statelessSession = sessionFactory.openStatelessSession(DataSourceUtils.getConnection(...));
// repeat many times
try {
    statelessSession.insert(entity);
}
catch (ConstraintViolationException e) {
    statelessSession.update(entity);
}
// some more actions
// close transaction

В SQL Server все работает так, как ожидалось. Каждый вызов session.insert(...) запускает SQL и, возможно, сбой по уникальному ограничению, если я пытаюсь вставить существующую строку. Когда я в конечном итоге совершаю транзакцию, все работает так, как я ожидаю.

Однако в Oracle ничего не происходит. Журналы Hibernate печатают SQL, но он просто не запускается. Кроме того, вставки не сбой, когда они должны.

После отладки кода Hibernate я узнал, что он связан с поведением JDBC. Я решил явно очистить statelessSession с помощью managedFlush(). Даже тогда я узнал, что я должен очистить его после каждой команды, иначе он не будет демонстрировать неудачное поведение, которое я хочу.

statelessSession = sessionFactory.openStatelessSession(DataSourceUtils.getConnection(...));
// repeat many times
try {
    statelessSession.insert(entity);
    ((Context) session).managedFlush()
}
catch (ConstraintViolationException e) {
    statelessSession.update(entity);
    ((Context) session).managedFlush()
}
// some more actions not related to statelessSession
// close transaction

Хотя это работает, когда я профилировал свой код, я обнаружил, что теперь он работает медленнее. Даже на SQL Server, где, по-видимому, метод managedFlush() не имеет ничего общего.

Любая идея, что происходит? Есть ли скрытая конфигурация, которую я могу настроить для достижения желаемого поведения - что-то вроде "авто-флеш" (но без автоматической фиксации, конечно)?

Теги:
spring
hibernate
orm

2 ответа

1

Во-первых, вы должны закрыть сеанс, как только получите исключение, потому что контекст сохранения может остаться в несогласованном состоянии.

Поэтому вы не должны обновлять ни одно исключение.

Цитирование сессии JavaDoc:

Если сеанс вызывает исключение, транзакция должна быть отброшена и сеанс отброшен. Внутреннее состояние сеанса может не соответствовать базе данных после возникновения исключения.

В вашем примере вы можете просто вызвать merge, потому что он работает как в sert/updates:

statelessSession.merge(entity);

Обновить

Поскольку вы используете MS SQL, вы можете использовать генератор IDENTITY, который отключает доработку JDBC. Последние версии MS SQL добавили поддержку Seqeuneces, которые более дружелюбны с пакетной загрузкой в любом случае.

Вам необходимо включить поддержку пакетной поддержки Hibernate, например:

hibernate.jdbc.batch_size=30

Если вы не устанавливаете это свойство, значение по умолчанию равно 0, поэтому нет пакетной загрузки JDBC по умолчанию.

  • 0
    Javadoc, которые вы цитируете, находятся на обычном сеансе, а не на сеансе без сохранения состояния. Я с ними очень знаком, но здесь дело не в этом. Спасибо, Йони
  • 0
    Проверьте мой обновленный ответ.
Показать ещё 1 комментарий
0

Почему работает на SQL Server:
SQL-сервер поддерживает READ UNCOMMITTED уровень изоляции. Это означает, что другая транзакция может видеть незафиксированные изменения в таблице.

Почему бы не работать с Oracle:
Oracle не поддерживает READ UNCOMMITTED, он имеет только READ COMMITTED и SERIALIZABLE. Таким образом, другая транзакция из "Oracle SQL Developer" или "Жаба" не может видеть все еще незафиксированные изменения транзакции в вашем приложении.

Давайте скажем "hibernate.jdbc.batch_size = 20", и вы хотите сделать 100 вставок. Ваше приложение отправит вставки как 5 партий, каждая из которых содержит 20 вставок. Другая транзакция "READ UNCOMMITTED" будет видеть каждую отдельную партию. Операция "READ COMMITTED" ничего не увидит, пока ваше приложение не совершит транзакцию, которая создает вставки.

  • 0
    Это не связано. У меня есть только одна транзакция в моем сценарии использования. Скажем, я вставляю 5000 записей. Я знаю, что некоторые из них существуют и потерпят неудачу, но я ожидаю, что сбой произойдет сразу же после запуска команды вставки SQL, чтобы я мог выполнить обновление.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню