我收到异常org.hibernate.PersistentObjectException:传递给persist的分离实体。从本论坛和其他地方的众多帖子中,我了解到这种情况发生在两种情况下(不考虑One-One注释等),
我发现我的代码中没有发生这些情况。我无法重现错误,因为我没有最初触发它的数据。在其他数据上,它运行得很好。我在下面提供了SCCE:
public class MyProcessor {
private MyImportEJB myEJB = MyImportEJB.getInstance();
private List<MyClass> saveQueue = new ArrayList<MyClass>();
public void process() {
List<X> rawData = getListOfX();
for(X x:rawData) {
processX();
}
saveFoos(saveQueue);
}
public void saveOrUpdateFoos(List<Foo> foos) {
for(MyClass foo:foos) {
MyClass existingFoo = myEJB.getFoosForWidAndDates(foo.getWid(), foo.getEffBeginDt(),foo.getEffEndDt());
if(existingFoo == null) saveQueue.add(foo);
else {
existingFoo.updateIfDifferent(foo);
saveQueue.add(existingFoo);
}
}
if(saveQueue.size() > 5000) {
myEJB.saveObjects(saveQueue);
saveQueue.clear();
}
}
public void processX() {
ArrayList<MyClass> foos = new ArrayList<MyClass>();
if(x.reportPeriod != null && x.gravity != null){
MyClass foo = new MyClass();
foo.setId(null);
foo.setWId(x.getWid());
foo.setEffBeginDt(x.reportPeriod);
foo.setEffEndDt(addOneMonth(x.reportPeriod));
foo.setGravity(x.gravity);
foos.add(foo);
}
saveOrUpdateFoos(foos);
}
}
MyImportEJB.java:
@Stateless
@EJB(name = "MyImportEJB", beanInterface = MyImportEJB.class)
@TransactionAttribute(TransactionAttributeType.SUPPORTS)
@PermitAll
public class MyImportEJB{
@Override
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void saveObjects(List<? extends P> mappedObjects)
{
for (P mappedObject : mappedObjects)
{
this.saveObject(mappedObject);
}
}
@Override
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void saveObject(P mappedObject)
{
EntityManager entityManager = this.getEntityManager();
Object identifier = this.getEntityManagerFactory().getPersistenceUnitUtil().getIdentifier(mappedObject);
if (identifier != null) {
Object existingObject = entityManager.find(mappedObject.getClass(), identifier);
if (existingObject != null) {
entityManager.merge(mappedObject);
return;
}
}
entityManager.persist(mappedObject);
}
public MyClass getFoosForWidAndDates(Integer wid, Calendar effBeginDt, Calendar effEndDt) {
try {
return (MyClass)((this.entityManager
.createQuery("select M from MyClass M where wid = :wid and effBeginDt = :effBeginDt and effEndDt = :effEndDt ", MyClass.class)
.setParameter("wid",wid)
.setParameter("effBeginDt", effBeginDt)
.setParameter("effEndDt", effEndDt)).getSingleResult());
} catch(NoResultException | NonUniqueResultException e) {
return null;
}
}
}
MyClass.java
public MyClass{
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name = "Id")
private Integer id;
@Column(name = "wid")
private Integer wId;
@Column(name = "eff_begin_dt")
private Calendar effBeginDt;
@Column(name = "eff_end_dt")
private Calendar effEndDt;
@Column(name = "gravity")
private Double gravity;
private Integer dataDownloadId;
public void updateIfDifferent(MyClass other) {
if(other.gravity != null && other.gravity != this.gravity) this.gravity = other.gravity;
//same for effBeginDt andeffEndDt
}
}
的persistence.xml
<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="ProdData">
<description>ProdData Database Persistence Unit</description>
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:jboss/ProdDataJNDI</jta-data-source>
<class>path.to.MyClass</class>
<class>path.to.X</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="hibernate.show_sql" value="false" />
</properties>
</persistence-unit>
</persistence>
调用entityManager.persist(mappedObject)&lt; - MyImportEJB.saveObject&lt; -MyImportEJB.saveObjects时抛出异常。我没有行号
我尝试编写一个示例程序,从数据库中获取一个existingFoo对象,更新并保存它,因为这是最可能的错误来源。但我无法重现错误。请帮忙。
编辑:以下是请求的getListofX()的详细信息
来自MyProcessor.java的:
public List<X> getListOfX() {
return myImportEJB.getUnprocessedIds(X.class, 30495);
}
来自文件MyImportEJB.java的:
@TransactionAttribute(TransactionAttributeType.SUPPORTS)
public List<Integer> getUnprocessedIds(Class<? extends ProductionRawData> clazz, Integer dataDownloadId) {
String canonicalName = clazz.getCanonicalName();
String queryStr = "select id from " + canonicalName + " where datadownloadId = :dataDownloadId and isProcessed != 1";
TypedQuery<Integer> query = this.entityManager.createQuery(queryStr, Integer.class)
.setParameter("dataDownloadId", dataDownloadId);
try {
return query.getResultList();
} catch(NoResultException nre) {
return new ArrayList<T>();
}
}
编辑:还添加了getFoosForWidAndDates()的详细信息。 有人告诉我,在将新Foo的id添加到保存队列之前,我将其设置为null。我想知道是否有可能正在设置ID&#34;引擎盖下#34;通过Hibernate达到一个不可接受的值
答案 0 :(得分:0)
我认为这可能是你的问题
if(saveQueue.size() > 5000) {
myEJB.saveObjects(saveQueue);
saveQueue.clear();
}
您正在填充saveQueue
最多5000个,然后您尝试保留/合并它们。我不确定Hibernate,但EclipseLink的默认共享缓存大小为1000.这意味着,您可以填充saveQueue
,例如,2000现有Foo
s,并且当您调用{时对它们{1}},它们不再位于实体管理器的缓存中。这导致在已经设置persist()
的已分离实例上调用persist()
。
在你的情况下,我认为在所有对象上调用id
是完全可以的,无论天气是否是现有对象,因为它会更新现有对象,并保留新对象。
答案 1 :(得分:0)
我希望您使用最新的Hibernate
。
我希望这是一个错字:
if(existingFoo == null) saveQueue.add(existingFoo); // if it is a null, you add it?
否则它看起来很有趣,你可以尝试做以下事情:
在每个(10,1000)持久化对象之后刷新会话,如下所示:
public void saveObjects(List<? extends P> mappedObjects)
{
for (P mappedObject : mappedObjects)
{
this.saveObject(mappedObject);
// some condition if you have a lot of objects
this.entityManager.flush();
}
}
使用不那么奇特的方式来检查实体是否已经存在:
public void saveObject(P mappedObject) {
EntityManager entityManager = this.getEntityManager();
if (mappedObject.getId() != null) {
entityManager.merge(mappedObject);
return;
}
entityManager.persist(mappedObject);
}
或者最好完全避免merge
并且只使用托管对象,这将需要所有操作的单个事务,因此需要重新设计事物。
答案 2 :(得分:0)
我再次在生产服务器上运行代码,并在分离的实体异常之前得到了TransactionRollbackException。我认为这可能是我观察到的例外的实际原因。
由于我试图保存的大量对象,事务超时并被回滚,这导致Foo对象变得分离。不知何故,TransactionRollbackException第一次没有出现在日志中,可能是因为它恰好发生在12:0 am UT。为了支持这一理论,在我将批量减小到~1000之后,该程序到目前为止还没有崩溃
对于那些感兴趣的人,TransactionRollbackException会导致实体分离:entityManager.getTransaction().rollback() detaches entities?
有些方法可以在发生这种情况时对此进行处理,但为了简单起见,我选择不执行它们