Spring JPA / Hibernate org.hibernate.AssertionFailure: null id in Entity (don't clear session after exception is thrown)

Experts / GURU / Friends

Our application is running Spring 3.2, JPA 2, Hibernate 4.2 tech stack with MySQL and Tomcat 7. We get a strange exception, which is quite a puzzle to solve. We have a very simple object that works well through the Junit test without any problem. But when I added Hibernate EmptyInterceptor (moved general company logic here) I got the below exception. Alternatively, I even tried to use Hibernate PreInsertEventListener, but this is the same exception.

After reading a couple of posts on stackoverflow - this and this and a few others , it makes me think that a read operation using the EntityManager (in the Interceptor) triggers an automatic reset that throws an exception. But I could not determine what it was. Also I am removing any non-null constraints in this simple Entity class as well as the table (which doesn't even have a foreign key).

MYSQL table

    CREATE TABLE  `rcent_rel_2`.`worklist_status_master` (
              `worklist_status_seqid` int(10) unsigned NOT NULL AUTO_INCREMENT,
              `worklist_status_name` varchar(45) DEFAULT NULL,
              `created_by` varchar(45) DEFAULT '',
              `updated_ts` datetime DEFAULT NULL,
              `updated_by` varchar(45) DEFAULT '',
              `comp_seq_id` int(10) unsigned DEFAULT NULL,
              `created_ts` datetime DEFAULT NULL,
              PRIMARY KEY (`worklist_status_seqid`)
            ) ENGINE=InnoDB AUTO_INCREMENT=65 DEFAULT CHARSET=latin1;

      

Simple essence

@Entity
@Table(name="worklist_status_master")
public class WorklistStatusDO  implements Serializable {
    private static final long serialVersionUID = 1L; 
    private static final Logger log = LoggerFactory.getLogger(WorklistStatusDO.class);

    private Integer id;
    private String workListStatusName;

    @Id
    //@GeneratedValue(strategy = GenerationType.IDENTITY) -- Tried this too
    @GeneratedValue
    @Column(name="worklist_status_seqid")
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Column(name="worklist_status_name")
    public String getWorkListStatusName() {
        return workListStatusName;
    }
    public void setWorkListStatusName(String workListStatusName) {
        this.workListStatusName = workListStatusName;
    }
    public WorklistStatusDO workListStatusName(String workListStatusName) {
        setWorkListStatusName(workListStatusName);
        return this;
    }

    @Override
    public String toString(){
        return Objects.toStringHelper(this).
                add("workListStatusName", getWorkListStatusName()).toString();
    }

    @Override
    public int hashCode() {
        return super.hashCode();
    }
    @Override
    public boolean equals(Object obj) {
        return (this == obj || (obj instanceof WorklistStatusDO && obj.hashCode() == hashCode()));
    }
}

      

Unit work script

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value =  "classpath*:/spring/applicationContext.xml")
@Transactional
public class WorklistStatusTest {

    @PersistenceContext
    private EntityManager entityManager;

    @Test
    public void saveWorklistStatus(){
        CompanyInfo.setCompanyName("ABC");
        UserInfo.setUserId("XYZ");

        WorklistStatusDO worklistStatus = new WorklistStatusDO();
        worklistStatus.workListStatusName("test");

        // Company Logic
        javax.persistence.Query query = entityManager.createQuery("Select c From CompanyMasterDO c Where c.companyName =:companyName");
        query.setParameter("companyName", CompanyInfo.getCompanyName());
        CompanyMasterDO companyMasterDO = (CompanyMasterDO) query.getSingleResult();

        assertThat(companyMasterDO).isNotNull();

        entityManager.persist(worklistStatus);
    }

}

      

Junit failure scenario due to below Exception after moving company logic to Interceptor

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value =  "classpath*:/spring/applicationContext.xml")
@Transactional
public class WorklistStatusTest {

    @PersistenceContext
    private EntityManager entityManager;

    @Test
    public void saveWorklistStatus(){
        CompanyInfo.setCompanyName("ABC");
        UserInfo.setUserId("XYZ");

        WorklistStatusDO worklistStatus = new WorklistStatusDO();
        worklistStatus.workListStatusName("test");

        // Company Logic moved to Interceptor

        entityManager.persist(worklistStatus);
    }

}

      

EmptyInterceptor

@Named
@Transactional
public class AuditEmptyInterceptor extends EmptyInterceptor {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public boolean onSave(Object entity, Serializable id, Object[] currentState,
            String[] propertyNames, Type[] types) {

        System.out.println("*********************inside OnSave() in Audit Empty Interceptor******************");
        javax.persistence.Query query = entityManager.createQuery("Select c From CompanyMasterDO c Where c.companyName =:companyName");
        query.setParameter("companyName", CompanyInfo.getCompanyName());
        CompanyMasterDO companyMasterDO = (CompanyMasterDO) query.getSingleResult();

        return false;
    }

      

Exception

    org.hibernate.AssertionFailure: null id in com.work.WorklistStatusDO entry (don't flush the Session after an exception occurs)
    at org.hibernate.event.internal.DefaultFlushEntityEventListener.checkId(DefaultFlushEntityEventListener.java:79)
    at org.hibernate.event.internal.DefaultFlushEntityEventListener.getValues(DefaultFlushEntityEventListener.java:194)
    at org.hibernate.event.internal.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:156)
    at org.hibernate.event.internal.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:228)
    at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:100)
    at org.hibernate.event.internal.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:58)
    at org.hibernate.internal.SessionImpl.autoFlushIfRequired(SessionImpl.java:1205)
    at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1262)
    at org.hibernate.internal.QueryImpl.list(QueryImpl.java:101)
    at org.hibernate.ejb.QueryImpl.getSingleResult(QueryImpl.java:287)
    at com.company.demo.audit.AuditEmptyInterceptor.onSave(AuditEmptyInterceptor.java:45)
    at com.company.demo.audit.StaticDelegateInterceptor.onSave(StaticDelegateInterceptor.java:24)
    at org.hibernate.event.internal.AbstractSaveEventListener.substituteValuesIfNecessary(AbstractSaveEventListener.java:387)
    at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:268)
    at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:192)
    at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:125)
    at org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:78)
    at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:208)
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:151)
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:78)
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:853)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:827)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:831)
    at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:875)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:241)
    at com.sun.proxy.$Proxy49.persist(Unknown Source)
    at com.rcent.test.worklist.WorklistStatusTest.saveWorklistStatus(WorklistStatusTest.java:42)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

      

Note:

There used to be a problem with the EmptyInterceptor injection. This has now been resolved on stackoverflow . Thanks Ricardo.

+3


source to share


2 answers


Try using Hibernate EventListener instead of Interceptors:

@Component
public class AuditEventListener implements PersistEventListener, DeleteEventListener, MergeEventListener, PreInsertEventListener {

  @PersistenceContext
  private EntityManager entityManager;

     @Override 
     public boolean onPreInsert(PreInsertEvent event) {
          return false; 
     } 

     @Override
     public void onPersist(PersistEvent event) {
        // you business logic
     }

     ... 
     ...
}

      

You can register a listener using this bean:

@Component
public class HibernateListenerRegistrar {
   @PersistenceUnit
   private EntityManagerFactory entityManagerFactory;

   @Autowired
   private AuditEventListener auditEventListener;

   @PostConstruct
   public void registerListeners() {
      if(entityManagerFactory instanceof HibernateEntityManagerFactory) {
          final HibernateEntityManagerFactory  hibernateEntityManagerFactory = (HibernateEntityManagerFactory) entityManagerFactory;
          final SessionFactoryImpl sessionFactory =  (SessionFactoryImpl) hibernateEntityManagerFactory.getSessionFactory();
          final EventListenerRegistry registry = sessionFactory.getServiceRegistry().getService(EventListenerRegistry.class);
          registry.getEventListenerGroup(EventType.PERSIST).appendListener(auditEventListener);
          registry.getEventListenerGroup(EventType.MERGE).appendListener(auditEventListener);
          registry.getEventListenerGroup(EventType.DELETE).appendListener(auditEventListener);
          registry.getEventListenerGroup(EventType.PRE_INSERT).appendListener(auditEventListener);
          // register other events here
      }
    }
}

      



Edit

This approach will not work if you want to execute queries during the PRE_INSERT event and your IDs are generated by the database. If you manually generate IDs (or retrieve them from the database prior to the transaction), you should be able to execute queries during PRE_INSERT without issue. I verified this with manually assigned IDs.

Depending on your audit requirements, you can probably implement the logic during the PERSIST event (which occurs after Hibernate has generated the IDs).

Hope it helps.

+3


source


The error indicates that no identifier is being generated. Also, some databases don't like to use column names called "id".

Try using the id generator annotation like this:

@GeneratedValue(strategy=GenerationType.AUTO, generator="seqMyObject")
@SequenceGenerator(name="seqMyObject", sequenceName="TBBROKERCONTACTSYNC_SEQ")

      

In the above snippet, I am using a database sequence.

Also let them go.



@Id
@GenericGenerator(name="gen",strategy="increment")
@GeneratedValue(generator="gen")
@Column(name = "ID", unique = true, nullable = false, precision = 15, scale = 0)
private Long id;

      

OR

@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;

      

MYSQL uses an identity strategy to grow automatically.

+1


source







All Articles