专家/大师/好友
我们的应用程序运行Spring 3.2,JPA 2,Hibernate 4.2技术堆栈与MySQL& Tomcat 7.我们得到一个奇怪的例外,这是一个很难解决的问题。我们有一个非常简单的实体,通过Junit测试很好地运行,没有任何问题。但是当我添加Hibernate EmptyInterceptor(在这里移动了常见的公司逻辑)时,我得到了下面提到的异常。或者我甚至尝试了Hibernate PreInsertEventListener但同样的异常。
在阅读了stackoverflow中的几篇帖子 - this和this以及few others之后,我认为使用EntityManager(在Interceptor中)的读取操作会触发自动刷新,从而导致异常被抛出但无法确定它是什么。此外,我删除了这个简单的Entity类以及表中的任何非null约束(它甚至没有外键)。
MYSQL表
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;
简单实体
@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失败的情况
@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;
}
异常
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)
注意:
之前有一个注入EmptyInterceptor的问题。现在解决了here in stackoverflow。感谢里卡多。
答案 0 :(得分:3)
尝试使用Hibernate EventListener而不是拦截器:
@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
}
...
...
}
您可以使用此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
}
}
}
修改
如果要在PRE_INSERT事件期间执行查询并且数据库生成ID,则此方法将不起作用。如果您手动生成ID(或在事务之前从数据库中获取它们),您应该能够在PRE_INSERT期间执行查询而不会出现问题。我已经使用手动分配的ID对此进行了测试。
根据您的审计要求,您可能在PERSIST事件期间(在Hibernate生成ID之后发生)实现逻辑。
希望这有帮助。
答案 1 :(得分:1)
错误表示未生成ID。此外,一些数据库不喜欢使用名为“id”的列名的用户。
尝试以这种方式使用id生成器注释:
@GeneratedValue(strategy=GenerationType.AUTO, generator="seqMyObject")
@SequenceGenerator(name="seqMyObject", sequenceName="TBBROKERCONTACTSYNC_SEQ")
在上面的代码片段中,我使用的是数据库序列。
也可以这样做。
@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使用身份策略自动增加。
答案 2 :(得分:0)
当DB是只读的并且尝试了写操作时,甚至可能发生此错误。