OpenJPA不规则地抛出EntityExistsException

时间:2016-09-23 10:55:23

标签: java jpa openjpa

我有一个新的(即尚未出现在数据库中)JPA实体,使用以下代码保存到DB:

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,因为我们使用对象的id来填充其他内容,并在数据库上生成id。

该实体如下所示:

@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: entities.Fund@1097fef5
        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: entities.Fund@1e7b1cce
        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,但问题仍然存在。

2 个答案:

答案 0 :(得分:2)

首先,在充分尊重的情况下,我不确定Prashant Katara的回答是什么。我不知道你需要做select / copy / persist的场景?克里斯提供了一些好评。虽然我不能确切地说出这个问题是怎么发生的,但是让我提供一些关于如何让自己陷入麻烦的信息,可能是零星的 - 或者特定于环境 - 正如你似乎指出的那样。在您的描述中,您发布了这种“保存”方法:

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

然后你说“我们需要刷新,因为我们使用id .....”。这里有一些关注的事情。如您所见,您合并传递给save的'entity'。但是,您永远不会返回从'em.merge'返回的对象。 OpenJPA可能不会填充'entity'中的id,而只会填充merge返回的id值!人们总是忘记传递给merge的实例不是托管实例!从合并返回的实例是管理的!既然你声明需要id的值,我假设你实际上在调用save之后使用'entity',因此你应该使用托管实例(即'em.merge'返回的实例)。这不是一个安全的操作,如果你在冲洗后调用fundId的getter方法,我很惊讶你不会得到'null'。如果你永远不要重复,请在调用后使用'entity'保存,然后我猜你的保存方法没问题。但是,如果您打算在保存后继续使用该实体,那么您的保存应如下所示:

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

最后,OpenJPA不会发出包含IDENTIFICATION字段的字段/列的INSERT。所以你永远不应该在其中看到带有fundId的INSERT。你的INSERT包含fundId的情况超出了我的范围。您可能在类路径上的某个地方有一个旧版本的Fund,它不包含'@GeneratedValue(strategy = GenerationType.IDENTITY)'。作为INSERT操作的一部分,IDENTIFICATION值从数据库返回,然后OpenJPA将返回的值分配给(MANAGED)IDENTIFICATION字段。

谢谢,

希斯

答案 1 :(得分:-1)

您需要首先检索对象,然后创建一个新对象,并将检索到的对象的属性复制到新对象,然后在新对象上调用persist方法。  您可以使用BeanUtils.copyProperties方法将属性从源对象复制到目标对象,也可以由您自己执行。