Зачем мне нужна транзакция в Hibernate для операций только для чтения?

107

Зачем мне нужна транзакция в Hibernate для операций только для чтения?

Блокирует ли БД следующая транзакция?

Пример кода для получения из БД:

Transaction tx = HibernateUtil.getCurrentSession().beginTransaction(); // why begin transaction?
//readonly operation here

tx.commit() // why tx.commit? I don't want to write anything

Могу я использовать session.close() вместо tx.commit()?

user93796
источник
9
Транзакция требуется самой БД. Вы можете прочитать о режиме
автоматической фиксации
@BheshGurung, я думаю, нам требуется трансканияция только для операции записи
user93796
4
Вы читали часть ссылки "Разоблачение мифов об автофиксации"?
Bhesh Gurung

Ответы:

131

На самом деле у вас могут быть причины отмечать транзакции как доступные только для чтения.

  1. Транзакции для чтения могут выглядеть действительно странно, и часто люди в этом случае не отмечают методы для транзакций. Но JDBC все равно создаст транзакцию, просто он будет работать, autocommit=trueесли другой параметр не был установлен явно.
  2. Но нет гарантии, что ваш метод не будет записывать в базу данных. Если вы пометите метод как @Transactional(readonly=true), Spring установит транзакцию JDBC в режим только для чтения, таким образом, вы будете определять, действительно ли возможна запись в БД в рамках этой транзакции. Если ваша архитектура громоздка и некоторые члены команды могут решить поместить запрос на изменение там, где он не ожидается, этот флаг укажет вам на проблемное место.
  3. Также БД могут оптимизировать транзакции только для чтения, но это, конечно, зависит от БД. Например, MySQL добавил поддержку этого только в InnoDB, начиная с версии 5.6.4.
  4. Если вы используете не JDBC напрямую, а ORM, это может быть проблематично. Например, сообщество Hibernate утверждает, что работа вне транзакции может вызвать непредсказуемое поведение. Это связано с тем, что Hibernate откроет транзакцию, но не закроет ее сам по себе, поэтому соединение будет возвращено в пул соединений, а транзакция не будет зафиксирована. Что происходит потом? JDBC хранит молчание, поэтому это зависит от реализации (MySQL откатывает транзакцию, Oracle ее фиксирует). Это также можно настроить на уровне пула подключений (например, C3P0 дает вам такую ​​возможность, откат по умолчанию).
  5. Еще одна вещь, когда дело доходит до Hibernate, Spring устанавливает FlushMode в MANUAL в случае транзакций только для чтения, что приводит к другим оптимизациям, таким как отсутствие необходимости в грязных проверках.
  6. Возможно, вы захотите переопределить или явно установить уровень изоляции транзакции. Это также влияет на транзакции чтения, поскольку вы хотите или не хотите читать незафиксированные изменения, подвергаться фантомному чтению и т. Д.

Подводя итог - можно пойти обоими путями, но нужно понимать последствия.

Станислав Башкирцев
источник
Спасибо за ответ. Я использую спящий режим (собственный спящий режим, а не jpa). Но спящий режим вынуждает меня начать преобразование перед выполнением какой-либо операции с базой данных. Если я не запустил его, он выдает ошибку, говоря, что нет активной трассировки. , если да, то как?
user93796
Флаг readonly фактически установлен для соединения, а не для самой транзакции, и вы не можете получить к нему доступ через Hibernate просто так. Вам необходимо использовать поддержку Spring Transaction и использовать либо аннотации, либо конфигурацию транзакций на основе XML. Таким образом, Spring берет на себя управление подключениями и транзакциями вместо Hibernate.
Станислав Башкирцев
1
Очень хорошо объяснено! Другая статья Марка Ричардса довольно хорошо объясняет подводные камни флага только для чтения в аннотации транзакций - ibm.com/developerworks/java/library/j-ts1/index.html .
Махеш
48

Все операторы базы данных выполняются в контексте физической транзакции, даже если мы явно не объявляем границы транзакции (BEGIN / COMMIT / ROLLBACK).

Если вы не объявляете границы транзакции явно, тогда каждый оператор должен будет выполняться в отдельной транзакции ( autocommitрежиме). Это может даже привести к открытию и закрытию одного соединения для каждого оператора, если ваша среда не может иметь дело с привязкой соединения к потоку.

Объявление службы as @Transactionalдаст вам одно соединение на весь период транзакции, и все операторы будут использовать это одно изолированное соединение. Это лучше, чем вообще не использовать явные транзакции.

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

JPA не применяет транзакции к операциям чтения. Только записи в конечном итоге вызывают исключение, необходимое для транзакции, если вы забыли запустить транзакционный контекст. Тем не менее, всегда лучше объявлять границы транзакций даже для транзакций только для чтения (в Spring @Transactionalпозволяет помечать транзакции только для чтения, что дает большое преимущество в производительности).

Влад Михалча
источник
1
«... тогда каждый оператор должен будет выполняться в отдельной транзакции». Контейнер не выполняет пакетную обработку автоматически?
Arash
Пакетная обработка предназначена для модификаций, а не для чтения данных. И даже в этом случае пакетная обработка JDBC не включена по умолчанию при использовании JPA и Hibernate.
Влад Михалча
1
Спасибо Влад. Я прочитал несколько ваших статей. Они действительно полезны.
Arash
Этот stackoverflow.com/q/34797480/179850, похоже, немного противоречит вашему утверждению о том, что транзакции только для чтения имеют большое преимущество в производительности. По крайней мере, в контексте гибернации это не так.
нимай
Нет, это не так. Речь идет о настройке только для чтения, а моя - об избежании автоматической фиксации.
Влад Михалча
18

Транзакции действительно блокируют базу данных - хорошие движки базы данных разумно обрабатывают параллельные блокировки - и полезны при использовании только для чтения, чтобы гарантировать, что никакая другая транзакция не добавляет данные, которые делают ваше представление несогласованным. Вы всегда хотите транзакцию (хотя иногда разумно настроить уровень изоляции, лучше не делать этого для начала); если вы никогда не пишете в БД во время транзакции, и фиксация, и откат транзакции будут одинаковыми (и очень дешевыми).

Теперь, если вам повезло и ваши запросы к БД таковы, что ORM всегда сопоставляет их с отдельными SQL-запросами, вы можете обойтись без явных транзакций, полагаясь на встроенное в БД поведение автофиксации, но ORM - относительно сложные системы. так что совершенно небезопасно полагаться на такое поведение, если только вы не проведете гораздо больше работы, проверяя, что на самом деле делает реализация. Написание явных границ транзакций намного проще сделать правильно (особенно если вы можете сделать это с помощью AOP или какой-либо подобной техники, основанной на ORM; я полагаю, что с Java 7 и далее можно использовать try-with-resources).

Donal Fellows
источник
14

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

Я видел ошибочные программы, убивающие огромные системы баз данных, потому что они просто читают данные, но никогда не фиксируют, заставляя журнал транзакций расти, потому что БД не может освободить данные транзакции до COMMIT или ROLLBACK, даже если клиент ничего не сделал часами.

Инго
источник