有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java OpenJPA不定期抛出EntityExistsException

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

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是在DB上生成的

实体如下所示:

@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会突然开始尝试为identity列插入一个值

我们使用的是OpenJPA 2.3.0版

我已尝试更新到2.4.1版,但问题仍然存在


共 (2) 个答案

  1. # 1 楼答案

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

  2. # 2 楼答案

    首先,恕我直言,我不确定普拉尚特·卡塔拉的答案是什么。我不知道有哪个场景需要执行选择/复制/持久化??克里斯提供了一些好的评论。虽然我不能确切地说这个问题是如何发生的,但请允许我提供一些信息,说明你是如何陷入麻烦的,可能是零星的,或者是特定于环境的,正如你似乎指出的那样。在您的描述中,您发布了以下“保存”方法:

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

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

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

    最后,OpenJPA不会发出包含标识字段的字段/列的INSERT。所以你不应该看到一个带有fundId的插件。我无法理解你是如何进入插入包含fundId的情况的。这可能是因为你的类路径上可能有一个旧版本的Fund,它不包含“@GeneratedValue(strategy=GenerationType.IDENTITY)”。作为插入操作的一部分,标识值从数据库返回,然后OpenJPA将返回的值分配给(托管的)标识字段

    谢谢

    希思