JPA EclipseLink с @TableGenerator. Почему значение, присвоенное id, не равно null после отката транзакции?

У меня есть следующая сущность:

@Entity
@Table(name = "sales", schema = "cust_tables")
@Multitenant(MultitenantType.TABLE_PER_TENANT)
@TenantTableDiscriminator(contextProperty = "customer_schema", type = TenantTableDiscriminatorType.SCHEMA)
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
public class Sale implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "Sale")
    @TableGenerator(name = "Sale", schema = "thehub", allocationSize = 5)
    @Column(name = "id", unique = true, nullable = false, updatable = false)
    @XmlElement
    private Long id;

Во время операции сохранения я могу намеренно разрешить откат транзакции во время сбоя оптимистичной блокировки.

Однако я удивлен поведением JPA/EclipseLink во время этого сценария. JPA присваивает значение из последовательности id, как и ожидалось. Однако при откате это значение не «стирается». Затем в следующий раз, когда я попытаюсь сохраниться, значение для id уже есть. JPA пропускает извлечение нового числа из последовательности и пытается сохранить сущность.

Я терплю неудачу с исключением ограничения целостности SQL:

Caused by: javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.0.v20130226-e0971b1): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '51' for key 'PRIMARY'
Error Code: 1062

Что я делаю неправильно?


person Jonathan S. Fisher    schedule 02.04.2013    source источник


Ответы (2)


Когда транзакция JPA терпит неудачу, весь объект становится отсоединенным. Вы не можете повторно отправить транзакцию.

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

JPA не предлагает простого способа повторной отправки транзакции. Собственный API EclipseLink предлагает commitAndResumeOnFailure(), но это не доступно для JPA. Возможно, зарегистрируйте запросы на улучшение, чтобы иметь какую-то опцию, позволяющую повторно отправить неудачную транзакцию в JPA.

person James    schedule 03.04.2013
comment
Спасибо за ваш комментарий, хотя повторная отправка транзакции не была проблемой. - person Jonathan S. Fisher; 03.04.2013

Хорошо, мне удалось это выяснить, и я опубликую свои результаты здесь, чтобы помочь всем, кто может столкнуться с той же проблемой.

Используя отладчик, я вижу, что EclipseLink действительно присваивает значение полю entity.id даже во время отката транзакции. Если вы похожи на меня, ваши поля идентификатора аннотированы, и ваш идентификатор неизменяем, что означает, что есть только геттер, а не сеттер. EclipseLink может использовать отражение для присвоения значения полю, но открытый API предотвращает изменение поля id. (Это хорошая вещь').

EclipseLink, по задумке или из-за ошибки, присваивает значение полю ID и удаляет это значение из последовательности ID. Таким образом, откат транзакции вызовет дыру в ваших порядковых номерах (что на самом деле не такое уж большое преступление).

Имейте в виду, что EclipseLink считывает таблицу порядковых номеров, кэширует несколько порядковых номеров в памяти, а затем повторно считывает ее, когда кэш пуст, при фиксации начального значения для следующей последовательности.

Каким-то образом из-за комбинации откатов транзакций и перезапуска моего сервера таблица порядковых номеров отставала от фактического используемого порядкового номера. Итак, ошибка не в назначении идентификатора при откате транзакции, а в потере порядкового номера. В итоге я получил таблицу порядковых номеров со значением 50, но объект с наибольшим номером в моей базе данных был 51. Решение состояло в том, чтобы обновить таблицу порядковых номеров до удвоенного значения selectionSize(5), установив для нее значение 60 и отскочить. мой сервер.

К сожалению, я не могу воспроизвести этот баг. :( Я понятия не имею, как EclipseLink попал в неправильное состояние.

person Jonathan S. Fisher    schedule 03.04.2013
comment
Это может быть другая ошибка, но у меня были похожие проблемы с EclipseLink при выполнении DROP_AND_CREATE, который каким-то образом не удалял все и жаловался, что сущности уже существуют. Мне пришлось отказаться от схемы непосредственно из SQL. - person toto2; 03.04.2013
comment
EclipseLink обычно назначает порядковые номера вне контекста транзакции, поэтому выделенные идентификаторы никогда не переназначаются. Однако, если вы используете JTA или вызываете flush(), то выделение последовательности будет использовать текущую транзакцию. Это плохо, и решение состоит в использовании пула соединений последовательности. См. eclipse.org/eclipselink/documentation/2.4/ jpa/расширения/ - person James; 04.04.2013
comment
документация там не очень хорошая, не могли бы вы рассказать об этом немного подробнее в ответе? У меня есть flush() в коде, чтобы принудительно проверить некоторые уникальные ключи. - person Jonathan S. Fisher; 04.04.2013