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版,但问题仍然存在
# 1 楼答案
您需要首先检索对象,然后创建一个新对象,并将检索到的对象的属性复制到新对象,然后在新对象上调用persist方法。 可以使用
BeanUtils.copyProperties
方法将属性从源对象复制到目标对象,也可以自己复制# 2 楼答案
首先,恕我直言,我不确定普拉尚特·卡塔拉的答案是什么。我不知道有哪个场景需要执行选择/复制/持久化??克里斯提供了一些好的评论。虽然我不能确切地说这个问题是如何发生的,但请允许我提供一些信息,说明你是如何陷入麻烦的,可能是零星的,或者是特定于环境的,正如你似乎指出的那样。在您的描述中,您发布了以下“保存”方法:
然后你说“我们需要冲水,因为我们使用id……”。这里有一些值得关注的事情。如您所见,您合并了传递到save中的“entity”。但是,您永远不会返回从“em.merge”返回的对象。OpenJPA很可能不会在“实体”中填充id,而是只填充merge返回的id值!人们总是忘记传递给merge的实例不是托管实例!合并返回的实例已被管理!由于您声明需要id的值,我假设您实际上在调用save之后使用了“entity”,因此您应该使用托管实例(即“em.merge”返回的实例)。这不是一个安全的操作,我很惊讶,如果在刷新之后调用fundId的getter方法,也不会得到“null”。如果你从来没有,重复从来没有,在调用后使用“实体”来保存,那么我想你的保存方法很好。但是,如果要在保存后继续使用该实体,则保存时应如下所示:
最后,OpenJPA不会发出包含标识字段的字段/列的INSERT。所以你不应该看到一个带有fundId的插件。我无法理解你是如何进入插入包含fundId的情况的。这可能是因为你的类路径上可能有一个旧版本的Fund,它不包含“@GeneratedValue(strategy=GenerationType.IDENTITY)”。作为插入操作的一部分,标识值从数据库返回,然后OpenJPA将返回的值分配给(托管的)标识字段
谢谢
希思