I get an org.hibernate.PersistentObjectException exception: a separate object is passed in for saving. From the many posts on this forum and elsewhere, I understand that this happens in two cases (not taking into account One-One annotations, etc.),
- There is a problem with a transaction going out of scope
- The identifier is set where it should be automatically generated.
I do not see anything like this with my code. I cannot reproduce the error because I do not have the data that originally caused it. According to other sources, it works fine. I have provided SCCE below:
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;
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>
An exception is thrown when the entityManager.persist (mappedObject) <- MyImportEJB.saveObject <-MyImportEJB.saveObjects method is called. I do not have a line number
I tried to write a sample program where I get the existingFoo object from the database, update and save it, because it was the most likely source of the error. But I could not reproduce the error. Please, help.
EDIT: Here are the details of getListofX () on request
from MyProcessor.java:
public List<X> getListOfX() { return myImportEJB.getUnprocessedIds(X.class, 30495); }
from the file 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 info on getFoosForWidAndDates (). I was suggested that I set the identifier of the new Foo to null before adding it to the save queue. I would like to know if it is possible that the identifier is set under "hoodernate" under Hibernate to an unacceptable value