#java #sql #hibernate #orm #jpa
Первый вопрос. Какая связь существует между уровнем изоляции транзакций, параметрами
JPA Lock.READ / Lock.WRITE и версиями @Version (Оптимистическая блокировка)? Я более
менее понял по отдельности каждую из этих 3 разделов, но не понимаю, как они связаны
между собой. Выбранный уровень изоляции транзакций дает некие «гарантии» при проведении
операций с таблицами. Параметры Lock.READ/Lock.WRITE позволяют добиться блокировки
полей таблицы при проведении операций. Если я правильно понял, для этого как раз и
используются поле @Version. Но все таки не до конца улавливаю связь между эти тремя
понятиями. Или они дополняют друг друга, или это разные способы решения одних и тех
же проблем. В общем, буду очень благодарен за ответ или же ссылку, где можно прочесть
информацию именно по вопросы связи между этими понятиями и их совместном использовании.
Второй вопрос. Каким способом в Hibernate можно указать требуемый уровень изоляции
транзакции для конкретной транзакции? И принято ли так делать?
Спасибо.
Ответы
Ответ 1
Попробую ответить по порядку:
Как соотносятся Lock и уровни изоляции
Уровень изоляции транзакций (transaction isolation level) - относится к чтению данных,
но не к записи данных;
Lock относится к доступу к данным (то есть затрагивает и чтение и запись и модификацию);
Можно сказать так, что механизм Lock обеспечивает работоспособность различных уровней
изоляции данных, в самом тупом примере: если у вас стоит уровень изоляции READ_COMMITTED,
то вставка произведенная параллельной сессией будет локирована на запись пока сессия
не выдаст commit(), но лок на чтение будет открыт сразу при запросе вашего select,
а при REPEATABLE_READ оба лока (на чтение и запись) будут стоять до commit() - соответственно
ваш select увидит результаты модификации только после коммита.
Каким способом в Hibernate можно указать требуемый уровень изоляции транзакции для
конкретной транзакции?
Вообще это не сильно здорово. Уровень изоляции транзакций это принадлежность коннекта
к БД, соответственно фактически изменение уровня изоляции равносильно переконнекту
к БД, а насколько это дорогая операция вы сами можете оценить исходя из имеющейся у
вас ситуации.
Тем не менее, если вы таки решили менять перед каждой транзакцией уровень изоляции
транзакций, то самый простой способ это отправить в СУБД raw SQL query (любой ORM это
поддерживает), типа:
ALTER SESSION SET ISOLATION_LEVEL SERIALIZABLE
Ответ 2
Вопрос лёгкий, но ответ слишком длинный. Лучше книжки почитать.
Уровни изоляции, блокировки настоящие и оптимистические - работают каждый над своей
проблемой. Прямо они не связаны. Подбирая их комбинацию, получаете то, что Вам нужно.
В jpa предусмотрены как оптимистичные режимы блокировки (полностью полагаются на
@Version), так и пессимистичные (которые выполняются сервером базы данных):
OPTIMISTIC (он же READ)
OPTIMISTIC_FORCE_INCREMENT (он же WRITE)
PESSIMISTIC_READ
PESSIMISTIC_WRITE
PESSIMISTIC_FORCE_INCREMENT
Как изменить уровень изоляции.
Если управляете транзакцией самостоятельно, то и меняйте как хотите (сonnection.setTransactionIsolation?
Нужно только connection извлечь из entity из managera). А например JTA изменить вряд
ли получится. Там уровень задаётся в конфигурации datasource и изменение его не предполагается.
Кажется именно так. Хотя можете уточнить этот момент. Всё течёт, всё изменяется. Может
это уже не так.
Ответ 3
Некоторую информацию по данному вопросу можно почерпнуть из статьи с хабра
UPD по второму вопросу:
Аннотация @Transactional указывает, что метод должен выполняться в транзакции. Менеджер
транзакций открывает новую транзакцию и создаёт для неё экземпляр Session, который
доступен через sessionFactory.getCurrentSession(). Все методы, которые вызываются в
методе с данной аннотацией, также имеют доступ к этой транзакции, потому что экземпляр
Session является переменной потока (ThreadLocal). Вызов sessionFactory.openSession()
откроет совсем другую сессию, которая не связана с транзакцией.
Параметр rollbackFor указывает исключения, при выбросе которых должен быть произведён
откат транзакции. Есть обратный параметр — noRollbackFor, указывающий, что все исключения,
кроме перечисленных, приводят к откату транзакции.
Параметр propagation самый интересный. Он указывает принцип распространения транзакции.
Может принимать любое значение из перечисления org.springframework.transaction.annotation.Propagation.
Propagation.REQUIRED — выполняться в существующей транзакции, если
она есть, иначе создавать новую.
Propagation.MANDATORY — выполняться
в существующей транзакции, если она есть, иначе генерировать
исключение.
Propagation.SUPPORTS — выполняться в существующей
транзакции, если она есть, иначе выполняться вне транзакции.
Propagation.NOT_SUPPORTED — всегда выполняться вне транзакции. Если
есть существующая, то она будет остановлена.
Propagation.REQUIRES_NEW — всегда выполняться в новой независимой
транзакции. Если есть существующая, то она будет остановлена до
окончания выполнения новой транзакции.
Propagation.NESTED — если есть текущая транзакция, выполняться в новой, так называемой,
вложенной транзакции. Если вложенная транзакция будет отменена, то
это не повлияет на внешнюю транзакцию; если будет отменена внешняя
транзакция, то будет отменена и вложенная. Если текущей транзакции
нет, то просто создаётся новая.
Propagation.NEVER — всегда выполнять
вне транзакции, при наличии существующей генерировать исключение.