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

Зачем мне нужна транзакция в 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()?

Ответ 1

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

  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. Вы можете переопределить или явно установить уровень изоляции транзакции. Это также влияет на операции чтения, поскольку вы делаете или не хотите читать незафиксированные изменения, подвергаетесь фантомному чтению и т.д.

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

Ответ 2

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

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

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

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

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

Ответ 3

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

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

Ответ 4

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

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