在查询周围使用 JPA 事务时,Hibernate Integrator 会导致刷新

Hibernate Integrator Causes Flush When Using JPA Transactions Around Queries

我正在为 Hibernate 开发 Integrator(Integrators 的背景:https://docs.jboss.org/hibernate/orm/4.3/manual/en-US/html/ch14.html#objectstate-decl-security) that by using listeners is supposed to take my data from how it's stored in the DB and convert it into a different form for processing at runtime. This works great when saving the data using .persist() however there's an odd behavior involving transactions. The following code is from Hibernate's own quickstart tutorial code

// now lets pull events from the database and list them
entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
List<Event> result = entityManager.createQuery( "from Event", Event.class ).getResultList();
for ( Event event : result ) {
  System.out.println( "Event (" + event.getDate() + ") : " + event.getTitle() );
}
entityManager.getTransaction().commit();
entityManager.close();

请注意异常事务 begin/commit 将查询包装到 select 数据。 运行 这会在查询完成后给出以下输出:

01:01:59.111 [main] DEBUG org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(175) - committing
01:01:59.112 [main] DEBUG org.hibernate.event.internal.AbstractFlushingEventListener.prepareEntityFlushes(149) - Processing flush-time cascades
01:01:59.112 [main] DEBUG org.hibernate.event.internal.AbstractFlushingEventListener.prepareCollectionFlushes(189) - Dirty checking collections
01:01:59.114 [main] DEBUG org.hibernate.event.internal.AbstractFlushingEventListener.logFlushResults(123) - Flushed: 0 insertions, 2 updates, 0 deletions to 2 objects
01:01:59.114 [main] DEBUG org.hibernate.event.internal.AbstractFlushingEventListener.logFlushResults(130) - Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections
01:01:59.114 [main] DEBUG org.hibernate.internal.util.EntityPrinter.toString(114) - Listing entities:
01:01:59.114 [main] DEBUG org.hibernate.internal.util.EntityPrinter.toString(121) - org.hibernate.tutorial.em.Event{date=2015-07-28 01:01:57.776, id=1, title=Our very first event!}
01:01:59.114 [main] DEBUG org.hibernate.internal.util.EntityPrinter.toString(121) - org.hibernate.tutorial.em.Event{date=2015-07-28 01:01:58.746, id=2, title=A follow up event}
01:01:59.115 [main] DEBUG org.hibernate.SQL.logStatement(109) - update EVENTS set EVENT_DATE=?, title=? where id=?
Hibernate: update EVENTS set EVENT_DATE=?, title=? where id=?
01:01:59.119 [main] DEBUG org.hibernate.SQL.logStatement(109) - update EVENTS set EVENT_DATE=?, title=? where id=?
Hibernate: update EVENTS set EVENT_DATE=?, title=? where id=?
01:01:59.120 [main] DEBUG org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doCommit(113) - committed JDBC Connection
01:01:59.120 [main] DEBUG org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.close(201) - HHH000420: Closing un-released batch
01:01:59.121 [main] DEBUG org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.releaseConnection(246) - Releasing JDBC connection
01:01:59.121 [main] DEBUG org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.releaseConnection(264) - Released JDBC connection
01:01:59.121 [main] DEBUG org.hibernate.internal.SessionFactoryImpl.close(1339) - HHH000031: Closing

似乎由于 Integrator 对相关实体进行了修改,它被标记为 "dirty" 并且在提交这个奇怪的事务时,它绕过了我的事件监听器并将值写回格式错误!我深入研究了代码,结果发现 org.hibernate.event.internal.AbstractFlushingEventListener.flushEntities(FlushEvent, PersistenceContext) 在上面被调用并试图为 EventType.FLUSH_ENTITY 获取监听器。不幸的是,为此 EventType 添加的侦听器从未在我的 Integrator 中调用。在这种情况下,我如何编写 Integrator 才能正确运行,以便我可以 "undo" 我的实体在运行时发生的转换,而不是清除错误的值?

最终问题是由于添加了 EventListenerRegistry 的事件侦听器的 EventType 引起的。有效的是将 EventType.POST_LOAD 用于所有读取操作,并结合 EventType.PRE_UPDATEEventType.PRE_INSERT 用于调用辅助方法以相同方式处理两者的写入操作。

为防止在更新实体后进行不必要的写入,如果实体在 EntityEntry 中称为 loadedState 时重置用于跟踪的数据是个好主意。这是 Hibernate 4 中的私有字段,因此您需要使用反射,但在 Hibernate 5 中,它可以通过 getLoadedState() 方法使用。另一个问题是您需要更新 "state" 的值,这些值在实际通过 PreInsertEventPreUpdateEvent 将值刷新到数据库时使用,可以从 getState() 方法中检索每个都有定义。