Комментарии 4

Давайте разбираться, дабы не ввести добрых людей в заблуждение сим опусом.


Сначала отделим мясо от костей. JPA работает поверх RDBMS леера, и практически все сказанное относится к последнему, и к JPA не имеет никакого отношения. Транзакционность и изолированность данных обеспечивается в первую очередь самой базой данных, а не JPA, которая играет роль лишь обертки ввиде меппера данных и генератора SQL. Теперь:


READ UNCOMMITED — решается с помощью аннотации Version в JPA(об этом как раз и статья)

Это еруйня. Потерянные обновления не возникают в модели RDBMS уже на самом низком уровне READ UNCOMMITED. JPA Version здесь абсолютно ни при чем. К тому же JDBC и в большинство RDBMS по дефолту работают на уровне READ COMMITED.


REPEATABLE READ — при повторном чтении получаются те же данные, что и в начале транзакции. Однако возможна пропажа/добавление рядов от другой транзакции при SELECT… WHERE ..., когда данные удовлетворяют критерию WHERE. Плюс другие транзакции все-же могут изменять прочитанные данные (но не измененные).


SERIALIZABLE — последовательное выполнение транзакций

Симуляция последовательного выполнения с возможностью спонтанного отката ввиду нарушения блокировки. И тем не менее, некоторые вендоры, которые используют схему MVCC, понижают SERIALIZABLE уровень до т.н. "SNAPSHOT" isolation, в котором возможен неприятный феномен типа write-skew.


Теперь вернемся к JPA.


Version была придумана во-первых для того, чтобы можно было отслеживать изменения в Extended Persistence Context, в котором unit-of-work живет дольше одной транзакции, и где Version имплементирует классическую схему MVCC.


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

Разница во-первых в том, что пессимистичные блокировки проверяют соответствие на момент получения блокировки, и как правило для этого исполользуют средства RDBMS (SELECT FOR UPDATE) а оптимистичные — делают все проверки при коммите при помощи сравнения данных или маркеров изменений типа Version. А то, что вы сказали — уже следствие.


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

Тем не менее поле Version не обязательно. При его отсутствии многие JPA делают полное/частичное сравнение данных полей объектов.


LockModeType.OPTIMISTIC_FORCE_INCREMENT… Вопрос. Зачем? Если после коммита мы хотим еще «поколдовать» над этими же данными, и нам не нужны сторонние транзакции

Вообще не для этого. Это аналог WRITE LOCK для optimistic locking. Гарантирует, что прочитанная Entity не была изменена другой транзакцией до самого коммита, даже если данная транзакция ничего не меняла. Используется для каскадного трекинга изменений, когда поля самой Entity не меняются, но зато меняются ее дочерние entities. При LockModeType.OPTIMISTIC поле Version меняется и проверяется только, если entity была изменена.

Спасибо за комментарий!
JPA работает поверх RDBMS леера, и практически все сказанное относится к последнему, и к JPA не имеет никакого отношения. Транзакционность и изолированность данных обеспечивается в первую очередь самой базой данных, а не JPA, которая играет роль лишь обертки ввиде меппера данных и генератора SQL.
Я и не говорил, что JPA обеспечивает транзакционность и изолированность данных.
JPA — это спецификация, ниже которой находятся провайдер ORM(Hibernate например) -> JDBC -> JDBC-драйвер -> База данных. Хотя в эту цепочку можно добавить и Spring Data.

Касаемо REPEATABLE READ, SERIALIZABLE вы расписали более подробно, хотя в статье я пометил их как «Коротко о...»

Разница во-первых в том, что пессимистичные блокировки проверяют соответствие на момент получения блокировки, и как правило для этого исполользуют средства RDBMS (SELECT FOR UPDATE) а оптимистичные — делают все проверки при коммите при помощи сравнения данных или маркеров изменений типа Version. А то, что вы сказали — уже следствие.
Да, действительно это следствие. Но о том, как происходит проверка я писал позже, поэтому это скорее стилистический косяк, нежели технический.
При его отсутствии многие JPA делают полное/частичное сравнение данных полей объектов.
Что? «Многие...» А можно примеры? Вы наверно имели ввиду «многие реализации этого стандарта», но в таком случае большинство Вашего негодования связано с тем, что читая JPA Вы представляете себе некую ORM.
При LockModeType.OPTIMISTIC поле Version меняется и проверяется только, если entity была изменена.

Хм… но как наша транзакция узнает, что entity была изменена другой транзакцией без сравнения полей Version. А если вы имели ввиду те транзакции, которые не меняли данных, то да вы правы. Хотя, это вроде как и так ясно. Если сильно все упростить, то Version — это версия данных.

Вообще не для этого. Это аналог WRITE LOCK для optimistic locking. Гарантирует, что прочитанная Entity не была изменена другой транзакцией до самого коммита, даже если данная транзакция ничего не меняла. Используется для каскадного трекинга изменений, когда поля самой Entity не меняются, но зато меняются ее дочерние entities.
Да, тут согласен, но тем не менее это ни как не исключает мой вариант. К слову, я бы тут добавил «когда изменение вносятся в другую сущность, связанную с исходной».

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

Негодование не поэтому. Впечатление от посыла вашей статьи — это "используйте Version и будет вам сериализуемое счастье", тогда для статьи требуется более глубокий анализ: расписать общий принцип работы локов и типичные нюансы испльзования Optimistic Locking в реальных программах.


Например, задайтесь вопросом, если у вас в Entity есть отношение @OneToMany, и вы добавляете/удаляете дочернюю сущность (пусть все сущности имеют Version), будет ли гарантия корректного параллельного выполнения? Поменяется ли Version для родительской сущности при удалении дочерней? Сразу скажу, что в JPA с каскадным локингом не все так просто, и многие вендоры добавляют свой функционал для обеспечения оного.
https://www.eclipse.org/eclipselink/documentation/2.5/jpa/extensions/a_optimisticlocking.htm#BCGIACHD


Что? «Многие...» А можно примеры? Вы наверно имели ввиду «многие реализации этого стандарта»

Вот пример в самой популярной ORM: http://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#locking-optimistic-versionless


Хм… но как наша транзакция узнает, что entity была изменена другой транзакцией без сравнения полей Version

А никак. LockModeType.OPTIMISTIC после чтения уже ничего не проверяет. Проверка и инкремент делается только, если entity была изменена текущей транзакцией. Если нужен более строгий контроль, используйте LockModeType.OPTIMISTIC_FORCE_INCREMENT — он всегда после чтения инкрементирует Version и делает в конце проверку, даже если entity не была изменена в текущей транзакции.


Если сильно все упростить, то Version — это версия данных

Не совсем. См. выше про LockModeType.OPTIMISTIC_FORCE_INCREMENT, там это тупо счетчик чтений.

C каскадным локингом не сталкивался, поэтому спасибо, что упомянули об этом.
Вот пример в самой популярной ORM: docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#locking-optimistic-versionless
Касаемо этой части. Я имел виду, что разновидностей JPA НЕТ, и быть не может. Это же стандарт. А вот реализации да, тот же хибер отличный пример.
А никак. LockModeType.OPTIMISTIC после чтения уже ничего не проверяет. Проверка и инкремент делается только, если entity была изменена текущей транзакцией.
Ну с этим я согласился еще в предыдущем комментарии.
Если нужен более строгий контроль, используйте LockModeType.OPTIMISTIC_FORCE_INCREMENT — он всегда после чтения инкрементирует Version и делает в конце проверку, даже если entity не была изменена в текущей транзакции.
Да, тут вы правы. Но это, вроде как, понятно из моих слов в статье:
значение поле Version принудительно увеличивается на 1.

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