Experts/Gurus/Friends
Our application runs with Spring 3.2, JPA 2, Hibernate 4.2 tech stack with MySQL & Tomcat 7. We are getting a weird exception which is quite a puzzle to solve. We have a very simple entity which works well through Junit test without any issues. But when I added Hibernate EmptyInterceptor (moved the common company logic in here) then I get the below mentioned exception.. Alternatively I even tried with Hibernate PreInsertEventListener also but same exception.
After reading few posts in stackoverflow - this and this and few others, it makes me to think that read operation using EntityManager (in Interceptor) is triggering auto flush which causes exception to be thrown. But couldn't identify what it is. Also I have remove any not null constrains in this simple Entity class as well as in table (which dont 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 Entity
@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()));
}
}
Junit working scenairo
@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 failing scenario due to below Exception after moving Company Logic out 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:
Had an issue earlier with injecting EmptyInterceptor. This is now solved here in stackoverflow. Thanks to Ricardo.
Try using a 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 the 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 won't work if you want to perform queries during the PRE_INSERT event and your IDs are generated by the database. If you manually generate Ids (or fetch them from the database before the transaction), you should be able to perform queries on during PRE_INSERT without problem. I've tested this using manually assigned IDs.
Depending on what your audit requirement is, you probably could implement the logic during the PERSIST event (which occurs after Hibernate has generated the IDs).
Hope this helps.
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句