от

Родовые травмы OpenJPA в Websphere7

При разработке энтерпрайз-приложений под Websphere Application Server естественным выбором при решении вопроса об используемой ORM будет OpenJPA — реализации JPA 2. Она поддерживается IBM и поставляется в составе JPA/OSGI feature pack. Но возникает проблема. Необходимость поддерживать тучу инсталляций, на которой крутятся приложения на сотни миллионов строк кода не добавляет IBM желания переходить на новые версии OpenJPA или хотя бы мерджить в свою базовую ветку исправление критических проблем. Ведь сотни разработчиков уже сидят на этих багах и втыкают в них свои костыли. Вот несколько проблем, с которыми мы сталкивались в своей работе

Instance … is not managed by this context

В принципе с такой ошибкой можно встретиться и без всяких багов. Напомню, что каждый модуль EJB в общем случае имеет свой persistence context, и если мы, по какой-то причине, пытаемся сохранять в базу объекты, которые пришли из разных контекстов мы можем запросо словить такой эксепшн. Это вполне нормально, так как эти объекты управляются разными брокерами JPA. Но сейчас речь не об этом. Мы получаем такую ошибку периодически при попытке сохранить список объектов, имеющих Lond id в качесвтве первичного ключа и односторонюю связь вида:

@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = «JOIN_COLUMN»)
private JoinedObject joinedObject;

Если такая сущность одна — нет проблем. Но если это список — openJPA при обходе связей второго объекта натыкается на наш связанный объект, идет в кэш свежих объектов и, увидев там связанных объект первой сущности, считает его уже сохраненным. То есть, по сути, не может их различить. В итоге, она падает при выполнении следующего рефреша.

Выход — заставить сделать flush первому объекту до сохрнения второго — то есть, по сути, заставить присвоить ему id (до flush id, например, из sequence базы еще не получен). Для этого я даже вынужден был придумать специальный костыль с рефлексией, для того, чтобы получить id (и где-то его использовать — например, вывести в трейс, чтобы операцию не съел оптимизатор).

Вообще, процесс поиска таких багов довольно интересный. Чтобы объяснять такие невнятные ошибки приходится долго и упорно дебажить OpenJPA, пытаясь разобраться в ее логике. Это может быть даже забавно, если время позволяет. На всякий случай, напомню, что в featurePack в 7 версии Websphere поставляется OpenJPA 2.0.1-SNAPSHOT. И хотя, у нас, конечно, нет доступа к ветке, из которой фичепак собирает IBM, для дебага можно ориентироваться на снепшот 2.0.x, как он представлен в основном репозитарии OpenJPA http://svn.apache.org/viewvc/openjpa/branches/2.0.x/

java.lang.ClassCastException: java.lang.String incompatible with java.lang.Integer

Еще один любопытный баг, который проявляется только при нагрузке. Вместо указаний на аттрибуты по номерам почему-то начинают попадаться аттрибуты по именам. В принципе, баг описан и зафикшен https://issues.apache.org/jira/browse/OPENJPA-1827 Но этот фикс вряд-ли когда-то попадет в фичепак. Решение парадоксальное — бороться с багом, возникающим при нагрузке, предлагается путем отключения кэширования готовых запросов. Отключается строчкой

<property name="openjpa.jdbc.QuerySQLCache" value="false"/>

в persistence.xml.

Получение лениво загружаемых полей сущностей после merge

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

Проблема проявляется следующим образом. Допустим есть сущность с неким списком, загружаемым «лениво». Получаем эту сущность — поле не подгружено. Делаем merge и читаем поле — получаем не ожидаемый сохраненный ранее в базе объект, а пустой список или даже null в зависимости от контекста транзакции. Если транзакция будет закоммичена после merge, получаем сохраненную ранее сущность через связь без проблем. Но в пределах одной транзакции это не работает.

Write a Comment

Комментарии