2016-09-23 3 views
0

У меня есть новый (т.е. нет в базе данных еще нет) JPA объекта, который сохраняется в БД с этим битом кода:OpenJPA метания EntityExistsException нерегулярно

public abstract class Dao { 
    @PersistenceContext(name = "puOpenJPA_Core",type = PersistenceContextType.TRANSACTION) 
    private EntityManager em; 

    public void save(Fund entity) { 
     entity = em.merge(entity); 
     em.flush(); 

     // do something with entity.fundId 
    } 
} 

Нам нужно flush, потому что мы используем идентификатор объекта, чтобы заполнить что-то еще, и идентификатор создается в БД.

Лицо выглядит следующим образом:

@Entity 
public class Fund extends AbstractFund implements Serializable 
{ 
    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @Column(name = "fundId") 
    protected Long id; 

    // other fields and getters etc. 

} 

AbstractFund является абстрактным сопоставляются суперкласса, который имеет несколько полей.

В таблице базы данных есть столбец fundId, определяемый как идентификатор. Это хорошо работает долгое время. Однако мы вызываем прерывистую ошибку, когда вызывается flush. После развертывания код отлично работает на некоторое время, а потом вдруг начинает бросать это исключение:

Caused by: <openjpa-2.3.0-r422266:1540826 fatal general error> org.apache.openjpa.persistence.PersistenceException: 
The transaction has been rolled back. See the nested exceptions for details on the errors that occurred. 
FailedObject: [email protected] 
     at org.apache.openjpa.kernel.BrokerImpl.newFlushException(BrokerImpl.java:2370) [openjpa-all-2.3.0.jar:2.3.0] 
     at org.apache.openjpa.kernel.BrokerImpl.flush(BrokerImpl.java:2207) [openjpa-all-2.3.0.jar:2.3.0] 
     at org.apache.openjpa.kernel.BrokerImpl.flushSafe(BrokerImpl.java:2105) [openjpa-all-2.3.0.jar:2.3.0] 
     at org.apache.openjpa.kernel.BrokerImpl.flush(BrokerImpl.java:1876) [openjpa-all-2.3.0.jar:2.3.0] 
     at org.apache.openjpa.kernel.DelegatingBroker.flush(DelegatingBroker.java:1045) [openjpa-all-2.3.0.jar:2.3.0] 
     at org.apache.openjpa.persistence.EntityManagerImpl.flush(EntityManagerImpl.java:663) [openjpa-all-2.3.0.jar:2.3.0] 
     at org.jboss.as.jpa.container.AbstractEntityManager.flush(AbstractEntityManager.java:457) [wildfly-jpa-8.1.0.Final.jar:8.1.0.Final] 
     ...... 
Caused by: java.lang.Exception: <openjpa-2.3.0-r422266:1540826 fatal store error> org.apache.openjpa.persistence.EntityExistsException: 
Cannot insert explicit value for identity column in table 'Fund' when IDENTITY_INSERT is set to OFF. {prepstmnt 2128975916 INSERT INTO Fund 
(fundId, ... other columns ...) VALUES (?, ?, ?,)} [code=544, state=23000] 
FailedObject: [email protected] 
     at org.apache.openjpa.util.Exceptions.replaceNestedThrowables(Exceptions.java:255) [openjpa-all-2.3.0.jar:2.3.0] 
     at org.apache.openjpa.persistence.PersistenceException.writeObject(PersistenceException.java:100) [openjpa-all-2.3.0.jar:2.3.0] 
     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [rt.jar:1.7.0_60] 
     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) [rt.jar:1.7.0_60] 
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) [rt.jar:1.7.0_60] 
     at java.lang.reflect.Method.invoke(Method.java:606) [rt.jar:1.7.0_60] 
     at org.jboss.marshalling.reflect.SerializableClass.callWriteObject(SerializableClass.java:271) 
     at org.jboss.marshalling.cloner.SerializingCloner.initSerializableClone(SerializingCloner.java:290) 
     at org.jboss.marshalling.cloner.SerializingCloner.clone(SerializingCloner.java:245) 
     at org.jboss.marshalling.cloner.SerializingCloner.clone(SerializingCloner.java:125) 
     at org.jboss.marshalling.cloner.SerializingCloner.cloneFields(SerializingCloner.java:341) 
     at org.jboss.marshalling.cloner.SerializingCloner.initSerializableClone(SerializingCloner.java:293) 
     at org.jboss.marshalling.cloner.SerializingCloner.initSerializableClone(SerializingCloner.java:277) 
     at org.jboss.marshalling.cloner.SerializingCloner.initSerializableClone(SerializingCloner.java:277) 
     at org.jboss.marshalling.cloner.SerializingCloner.initSerializableClone(SerializingCloner.java:277) 
     at org.jboss.marshalling.cloner.SerializingCloner.initSerializableClone(SerializingCloner.java:277) 
     at org.jboss.marshalling.cloner.SerializingCloner.clone(SerializingCloner.java:245) 
     at org.jboss.marshalling.cloner.SerializingCloner.clone(SerializingCloner.java:125) 
     at org.jboss.as.ejb3.remote.LocalEjbReceiver.clone(LocalEjbReceiver.java:314) [wildfly-ejb3-8.1.0.Final.jar:8.1.0.Final] 
     at org.jboss.as.ejb3.remote.LocalEjbReceiver.clone(LocalEjbReceiver.java:297) [wildfly-ejb3-8.1.0.Final.jar:8.1.0.Final] 
     at org.jboss.as.ejb3.remote.LocalEjbReceiver.processInvocation(LocalEjbReceiver.java:249) [wildfly-ejb3-8.1.0.Final.jar:8.1.0.Final] 
     ... 126 more 

Я был не в состоянии воссоздать это на других средах и нахожусь в недоумении, почему OpenJPA внезапно начинают пытаться вставить значение для столбца идентификации.

Мы используем OpenJPA версии 2.3.0.

Я пробовал обновление до версии 2.4.1, но проблема не устранена.

+0

Сомневаюсь, если таблица 'Fund' уже есть некоторые старые строки, которая вызывает проблему? –

+0

Кажется маловероятным - ошибка SQL весьма специфична, поскольку речь идет о вставке столбца идентификатора. –

+1

Ошибка явная, она заявляет, что не может вставить значение в поле ID. Вы вызываете слияние на сущности, у которой уже установлено поле идентификатора. Что вы объединяете, когда получаете это исключение, и вы можете попробовать включить SQL-журнал, чтобы вы могли видеть вставки/обновления/удаления, чтобы отслеживать, почему у этого есть проблема. Я предполагаю, что вы удалили сущность и пытаетесь объединить ее обратно, и поле идентификации не позволит вам. – Chris

ответ

-1

Вам необходимо сначала получить объект, затем создать новый объект и скопировать свойства из полученного объекта в новый объект и после этого вызвать метод persist для нового объекта. Вы можете использовать метод BeanUtils.copyProperties для копирования свойств из исходного объекта в целевой объект или вы можете сделать это самостоятельно.

+0

Я не был достаточно ясен, что объект новый, поэтому он не существует в БД. –

2

Во-первых, при всем моем уважении, я не уверен, куда направился Прашант Катара с его ответом. Я не знаю о сценарии, где вам нужно сделать select/copy/persist ?? Крис дал хорошие комментарии. Хотя я не могу точно сказать, как эта проблема возникла, позвольте мне предоставить некоторую информацию о том, как вы можете попасть в неприятности, возможно, спорадические или специфические для окружающей среды, - как вы, кажется, указываете. В описании вы вывесили эту «сохранить» метод:

public void save(Fund entity) { 
    em.merge(entity); 
    em.flush(); 
} 

тогда Вы сказали «Нам нужно собрать флеш, потому что мы используем идентификатор .....». Здесь есть несколько вещей. Как вы можете видеть, вы объединяете «entity», который передается в save. Однако вы никогда не возвращаете объект, который возвращается из «em.merge». OpenJPA, скорее всего, не заполнит идентификатор в «сущности», скорее, он будет ТОЛЬКО заполнять значение id, возвращаемое слиянием! Люди всегда забывают, что экземпляр, переданный для слияния, НЕ является управляемым экземпляром! Возвращается экземпляр, возвращаемый из слияния! Поскольку вы заявили, что вам нужно значение id, я предполагаю, что вы фактически используете «сущность» после вызова для сохранения, и поэтому вы должны использовать управляемый экземпляр (т. Е. Экземпляр, возвращаемый «em.merge»). Это не безопасная операция, и я удивлен, что вы не получите «null», если вы вызываете метод getter для fundId даже после флеша. Если вы НИКОГДА не повторяете НИКОГДА, используйте «сущность» после вызова, чтобы сохранить, тогда я думаю, что ваш метод сохранения в порядке. Тем не менее, если вы намерены продолжать использовать объект после сохранения, то ваш сохранить должен выглядеть следующим образом:

public Fund save(Fund entity) { 
    Fund entityPrime = em.merge(entity); 
    em.flush(); 
    return entityPrime; 
} 

Наконец, OpenJPA не выдает INSERT, который содержит поле/столбец для поля идентификации. Поэтому вы никогда не должны видеть INSERT с fundId в нем.Это вне меня, как вы попали в случай, когда ваш INSERT содержит fundId. Возможно, у вас, возможно, есть старая версия фонда где-то на вашем пути к классу, который не содержит «@GeneratedValue (strategy = GenerationType.IDENTITY)». В рамках операций INSERT значение IDENTIFICATION возвращается из базы данных, и OpenJPA затем присваивает это возвращаемое значение полю (ИДЕНТИФИКАЦИЯ).

Спасибо,

Хит

+0

Спасибо, Хит, что вы говорите, имеет смысл, и я согласен, что мы никогда не увидим fundId во вставке, что вызывает беспокойство. Я допустил ошибку в введенном мной коде. Мы присваиваем сущности значение, возвращаемое методом 'merge'. –

+0

Ок Шон, так что вы все еще видите ту же проблему с обновленным кодом (т. Е. Используя экземпляр, возвращаемый слиянием)? Если это так, я думаю, что единственный способ, которым я могу разобраться в этом, - это привязать (или вставить?) Все сущности и фрагмент вашего прецедента ... Мне нужно знать, когда Txs запускается/завершается, и когда вы делаете слияние/флеш (любой вызов entitymanager в этом случае). Я также предлагаю вам искать весь путь к классу для Fund.class .... учитывая спорадический (или env специфический) характер вашей проблемы. Интересно, может ли OpenJPA найти старые метаданные, ведущие к выводу, что fundId не является идентификатором. –

Смежные вопросы