简体   繁体   English

间歇性错误org.hibernate.PersistentObjectException:传递给persist的分离实体

[英]Intermittent error org.hibernate.PersistentObjectException: detached entity passed to persist

I am getting the exception org.hibernate.PersistentObjectException: detached entity passed to persist. 我收到异常org.hibernate.PersistentObjectException:传递给persist的分离实体。 From the numerous posts on this forum and elsewhere, I understand that this happens in two cases(not considering One-One annotations etc), 从本论坛和其他地方的众多帖子中,我了解到这种情况发生在两种情况下(不考虑One-One注释等),

  1. There is an issue with the transaction going out of scope 超出范围的交易存在问题
  2. An id is set where it should be automatically generated. 将id设置为应自动生成的位置。

I see neither of these happening with my code. 我看到这些都没有发生在我的代码中。 I am unable to reproduce the error, because I don't have the data which initially triggered it. 我无法重现错误,因为我没有最初触发它的数据。 On other data it runs perfectly fine. 在其他数据上,它运行得很好。 I have provided an SCCE below: 我在下面提供了一个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: 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 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 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>

The exception is thrown on calling entityManager.persist(mappedObject) <- MyImportEJB.saveObject <-MyImportEJB.saveObjects. 调用entityManager.persist(mappedObject)< - MyImportEJB.saveObject <-MyImportEJB.saveObjects时抛出异常。 I dont have the line number 我没有行号

I have tried writing a sample program where I get an existingFoo object from the database, update and save it, because that was the most likely source of the error. 我尝试编写一个示例程序,从数据库中获取一个existingFoo对象,更新并保存它,因为这是最可能的错误来源。 But I could not reproduce the error. 但我无法重现错误。 Please help. 请帮忙。

EDIT: Here are the details of getListofX() as requested 编辑:以下是getListofX()的详细信息

from MyProcessor.java: 来自MyProcessor.java:

public List<X> getListOfX() {
    return myImportEJB.getUnprocessedIds(X.class, 30495);
}

from the file MyImportEJB.java: 从文件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>();
    }
}

EDIT: Also added the details of getFoosForWidAndDates(). 编辑:还添加了getFoosForWidAndDates()的详细信息。 It was suggested to me that I set the id on a new Foo to null before adding it to the save queue. 有人告诉我,在将新Foo的id添加到保存队列之前,我将其设置为null。 I would like to know if it is possible that the id is being set "under the hood" by Hibernate to an unacceptable value 我想知道是否有可能将Hibernate的“引擎盖”设置为不可接受的值

I think this could be your issue 我想这可能是你的问题

 if(saveQueue.size() > 5000) {
     myEJB.saveObjects(saveQueue);
     saveQueue.clear();
 }

You are filling the saveQueue up to 5000, and then you try to persist / merge them. 您正在将saveQueue填充到5000,然后您尝试保留/合并它们。 I'm not sure about Hibernate, but EclipseLink's default shared cache size is 1000. That means, you could fill up saveQueue with, for example, 2000 existing Foo s and, by the time you call persist() on them, they are no longer in entity manager's cache. 我不确定Hibernate,但EclipseLink的默认共享缓存大小是1000.这意味着,你可以填充saveQueue ,例如,2000现有的Foo ,当你在它们上调用persist()时,它们就不是在实体管理器的缓存中更长。 This leads to calling persist() on detached instance which has id already set. 这导致在已经设置了id已分离实例上调用persist()

In your case, I think it would be perfectly OK to call entityManager.merge(mappedObject) on all objects, regardless of weather it is an existing object or not, because it will update existing ones, and persist new ones. 在你的情况下,我认为在所有对象上调用entityManager.merge(mappedObject)是完全可以的,无论天气是否是现有对象,因为它将更新现有对象,并持久保存新对象。

I hope you are using latest Hibernate . 我希望你使用最新的Hibernate And I expect that it is a typo here: 我希望这是一个错字:

if(existingFoo == null) saveQueue.add(existingFoo); // if it is a null, you add it?

Otherwise it looks quite interesting, you could try to do following things: 否则它看起来很有趣,你可以尝试做以下事情:

  1. Flush session after each (10, 1000) persisted objects, like that: 在每个(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(); } } 
  2. Use less exotic way to check if entity was persisted already: 使用不太奇特的方式来检查实体是否已经存在:

     public void saveObject(P mappedObject) { EntityManager entityManager = this.getEntityManager(); if (mappedObject.getId() != null) { entityManager.merge(mappedObject); return; } entityManager.persist(mappedObject); } 

    Or it is better completely avoid merge and just work with managed objects, this will require a single transaction for all operations, thus will require redesign of the things. 或者最好完全避免merge并且只使用托管对象,这将需要所有操作的单个事务,因此需要重新设计事物。

I ran the code on the production server again and I got a TransactionRollbackException right before the detached entity exceptions. 我再次在生产服务器上运行代码,并在分离的实体异常之前得到了TransactionRollbackException。 I think that this might be the actual cause of the exception I observed. 我认为这可能是我观察到的例外的实际原因。

The transaction timed out and was rolled back due to the huge number of objects I was trying to save, and this led to the Foo objects becoming detached. 事务超时并由于我试图保存的大量对象而被回滚,这导致Foo对象变得分离。 Somehow the TransactionRollbackException didnt show up in the logs the first time around, probably because it happened exactly at 12:0am UT. 不知何故,TransactionRollbackException第一次没有出现在日志中,可能是因为它恰好发生在12:0 am UT。 In support of this theory, the program hasn't crashed so far after I decreased the batch size to ~1000 为了支持这一理论,在将批量减小到~1000之后,该程序到目前为止还没有崩溃

For those interested, a TransactionRollbackException does lead to entities getting detached: entityManager.getTransaction().rollback() detaches entities? 对于那些感兴趣的人,TransactionRollbackException会导致实体分离: entityManager.getTransaction()。rollback()分离实体?

There are ways to handle this when it occurs, but I opted against implementing them for simplicity's sake 有可能在发生这种情况时处理这个问题,但为了简单起见,我选择不执行它们

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 错误:org.hibernate.PersistentObjectException:分离的实体传递给持久化 - Error: org.hibernate.PersistentObjectException: detached entity passed to persist Hibernate:org.hibernate.PersistentObjectException:传递给persist的分离实体 - Hibernate: org.hibernate.PersistentObjectException: detached entity passed to persist org.hibernate.PersistentObjectException:分离的实体传递给持久化-ManyToMany映射 - org.hibernate.PersistentObjectException: detached entity passed to persist - ManyToMany Mapping org.hibernate.PersistentObjectException:分离的实体传递到持久合并 - org.hibernate.PersistentObjectException: detached entity passed to persist MERGE org.hibernate.PersistentObjectException:传递给持久化 OrderProduct 的分离实体 - org.hibernate.PersistentObjectException: detached entity passed to persist OrderProduct org.hibernate.PersistentObjectException:分离的实体被传递以持久保存在新对象上 - org.hibernate.PersistentObjectException: detached entity passed to persist on new Object playframework org.hibernate.PersistentObjectException:传递给persist的分离实体:models - playframework org.hibernate.PersistentObjectException: detached entity passed to persist: models org.hibernate.PersistentObjectException:传递给持久异常的分离实体 - org.hibernate.PersistentObjectException: detached entity passed to persist exception org.hibernate.PersistentObjectException:分离的实体传递给持久域对象 - org.hibernate.PersistentObjectException: detached entity passed to persist domain object SPRING JPA-org.hibernate.PersistentObjectException:传递给持久化的分离实体: - SPRING JPA - org.hibernate.PersistentObjectException: detached entity passed to persist:
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM