Здравствуйте, Ballista, Вы писали:
B>JpaTransactionManager
Это хорошо и многое объясняет. Есть отличный последний параграф в
документации (выделение взято из оригинала):
This transaction manager supports nested transactions via JDBC 3.0 Savepoints. The "nestedTransactionAllowed" flag defaults to false though, since nested transactions will just apply to the JDBC Connection, not to the JPA EntityManager and its cached entity objects and related context. You can manually set the flag to true if you want to use nested transactions for JDBC access code which participates in JPA transactions (provided that your JDBC driver supports Savepoints). Note that JPA itself does not support nested transactions! Hence, do not expect JPA access code to semantically participate in a nested transaction.
Здесь почти все факты важны:
Вложенные транзакции вообще выключены по-умолчанию
Если они включены, то применяются только к JDBC
JPA вообще не поддерживает вложенные транзакции ни в каком виде.
Последнее в принципе понятно — определять семантику вложенных транзакций будет очень сложно. Что делать, если одно поле поменялось во внешней транзакции, а другое — во внутренней? Какое должно быть состяние объектов, если транзакция откатилась? И т.д.
B>я не пойму как события в моей логике могут влиять на спринг батч транзакцию, если они оформлены как совершенно отдельные транзакции. у моей логики отдельная транзакция.
А это потому, что связь ORM (это то, что бросается OptimisticLockingException) и JDBC (всякие *Template) сделана совершенно неочевидным образом. У вас
первичная транзакция управляется Entity Manager (т.е. ORM). Для доступа к JDBC используется то же соединение, что и для управления всеми сущностями (entity). Соединение в enity manager прокидывается "сразу", когда впервые создается для потока. Смотрите ближе к концу
здесь. Поэтому-то единственное, что вообще видит и может откатить entity manager — это верхняя транзакция. Даже если предположить, что все настроено так, что атрибуты транзакций не игнорируются, вы получите новую независимую транзакцию только для JDBC. А в enity manager так и останется та самая изначальная транзакция (разделяемая со внутренностями spring batch).
Почему сделали именно так — не знаю. В теории можно было бы устанавливать новый entity manager при возникновении новой транзакции. Но при этом остаются вопросы семантики (что делать, если мы смешиваем entity из двух разных транзакций?). И, вероятно, какие-то реализации entity manager используют "независимые от себя" thread local. Т.е. если заменить один экземпляр на другой, часть thread local и контекста все равно останется. Но в целом причины не важны. Результат мы уже знаем.
B>ну и второй вопрос — верно ли я понимаю, что при OptimisticLockingException я обязан повторять попытку ? словить OptimisticLockingException и пойти дальше получается нельзя.
Да, получается так. У вас "рано" инициализируется EntityManager и ORM-транзакция. Для ORM OptimisticLockingException является фатальным для всей транзакции. Поэтому цепляется и spring batch.
Продолжать можно было бы, если бы у вас entity manager инициализировался позже и на основе текущей транзакции. Т.е. в качестве transaction manager у вас был бы DataSourceTransactionManager. И уже потом, каким-то отдельным способом вы инициализировали EntityManager только в рамках отдельного запроса/контекста. Ну и как-то подсовывали его для всей автоматической машинерии в Spring. Создать entity manager вручную можно, смотрите второй ответ
здесь. Что дальше с ним делать — не знаю

. Можете явно методы вызывать. Можете пытаться куда-то подсунуть. Явный экземпляр хотя бы даст возможность вручную его транзакцией управлять. Что там будет нижележащее — текущая транзакция из JDBC или своя новая — тоже не знаю.