简体   繁体   English

Spring JPA/Hibernate org.hibernate.AssertionFailure: null id in Entity(异常发生后不刷新Session)

[英]Spring JPA/Hibernate org.hibernate.AssertionFailure: null id in Entity (don't flush the Session after an exception occurs)

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.我们的应用程序使用 Spring 3.2、JPA 2、Hibernate 4.2 技术堆栈和 MySQL 和 Tomcat 7 运行。我们遇到了一个奇怪的异常,这是一个需要解决的难题。 We have a very simple entity which works well through Junit test without any issues.我们有一个非常简单的实体,它通过 Junit 测试运行良好,没有任何问题。 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.但是,当我添加 Hibernate EmptyInterceptor(在此处移动了常见的公司逻辑)时,我得到了下面提到的异常。

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.在计算器阅读几篇文章后- 这个这个其他几个,这让我想到用EntityManager的是读操作(拦截)被触发从而导致引发异常自动刷新。 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 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;

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 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
        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将公司逻辑移出拦截器后,由于低于异常而导致 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;
    }

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.之前在注入 EmptyInterceptor 时遇到了问题。 This is now solved here in stackoverflow .这现在在 stackoverflow 中解决 Thanks to Ricardo.感谢里卡多。

Try using a Hibernate EventListener instead of Interceptors:尝试使用 Hibernate EventListener 而不是 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:您可以使用此 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.如果您想在 PRE_INSERT 事件期间执行查询并且您的 ID 由数据库生成,则此方法将不起作用。 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.如果您手动生成 Id(或在事务之前从数据库中获取它们),您应该能够在 PRE_INSERT 期间毫无问题地执行查询。 I've tested this using manually assigned IDs.我已经使用手动分配的 ID 对此进行了测试。

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).根据您的审计要求,您可能可以在 PERSIST 事件(发生在 Hibernate 生成 ID 之后)期间实现逻辑。

Hope this helps.希望这可以帮助。

The error indicates that an ID is not being generated.该错误表明未生成 ID。 Also some databases do not like user using column names called 'id'.还有一些数据库不喜欢用户使用名为“id”的列名。

Try using the id generator annotation in this way:尝试以这种方式使用 id 生成器注释:

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

In the above snippet I am using a database sequence.在上面的代码片段中,我使用了一个数据库序列。

Also give these a 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 the identity strategy to auto increment. MYSQL 使用身份策略自动递增。

这个错误甚至可能发生,当 DB 是只读的并且尝试写操作时。

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

相关问题 Hibernate AssertionFailure:条目中的id为null(发生异常后不要刷新Session) - Hibernate AssertionFailure: null id in entry (don't flush the Session after an exception occurs) Spring JPA 共享主键导致 org.hibernate.AssertionFailure: null identifier - Spring JPA Shared primary key causes org.hibernate.AssertionFailure: null identifier org.hibernate.AssertionFailure:集合被flush()处理了两次 - org.hibernate.AssertionFailure: collection was processed twice by flush() Hibernate:org.hibernate.AssertionFailure:com.xxx.Bean条目中的null id - Hibernate: org.hibernate.AssertionFailure: null id in com.xxx.Bean entry org.hibernate.AssertionFailure:可能对会话进行非线程安全的访问 - org.hibernate.AssertionFailure: possible non-threadsafe access to the session org.hibernate.AssertionFailure: null 标识符@OneToOne 关系 - org.hibernate.AssertionFailure: null identifier @OneToOne relationship 如何删除“空ID…条目(在发生异常后不要刷新会话)” - how to remove “null id …entry (don't flush the Session after an exception occurs)” org.hibernate.AssertionFailure 获取序列值时的问题 - org.hibernate.AssertionFailure Issue when getting sequence value org.hibernate.criteria.uniqueResult期间的AssertionFailure - org.hibernate.AssertionFailure during criteria.uniqueResult Spring3 /休眠声明失败 - Spring3/Hibernate AssertionFailure
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM