Hibernate @NamedNativeQuery 读取由 "normal" 实体写入的陈旧数据

Hibernate @NamedNativeQuery reads stale data written by "normal" entity persist

我 运行 遇到了一个 Java (Dropwizard) Web 服务的小问题 运行,该服务使用 Hibernate 读取和写入 MySQL。我可以总结的最好的方法是,当两个 t运行saction 都是在同一个会话中。调试时,写入对外部 MySQL 客户端可见。当每个 t运行saction 都在其自己的会话中时,所有读取都会看到一致的世界观。这几乎就像写入到 MySQL 但 NamedNativeQuery 正在从内存中的缓存版本读取。我会尝试更详细地解释...

为了描述这个问题,应用程序有三个 Hibernate 实体,它们使用相同的两个数据库 tables,我们只说 table X 和 Y 以及实体 A、B 和 C . 其中两个实体(A 和 B)是简单的,它们使用 AbstractDAOfrom Dropwizard)中的方法将 table 中的行映射到实体,以读写以及 HQL 和休眠查询 API。因此,table X 中的一行映射到实体 A 的一个实例,table Y 中的一行映射到实体 B 的一个实例。

第三个实体(实体C)有点不同。它实际上是只读的,旨在通过加入 tables X 和 Y 来收集一些聚合统计信息。它使用 @NamedNativeQuery 执行单个本机 MySQL 查询并映射到实体。此连接在 table X 上使用指向 table Y 的外键。

这是我看到的行为:

Session session = sessionFactory.openSession();
ManagedSessionContext.bind(session);

Transaction tx = session.beginTransaction();

EntityA a = daoA.read(); // reads from table X using HQL query
EntityC c = daoC.read() // reads from X and Y using NamedNativeQuery
a.setFoo(newFoo);
daoA.write(a); // write using AbstractDao.persist. updates table X

tx.commit();

Transaction tx = session.beginTransaction();
c = daoC.read() // reads X and Y using NamedNativeQuery again. does not see write to table X above^
                // while the app was paused in the debugger here, a MySQL client running the same native query sees the write when selecting from table X
tx.commit();

session.close();

此版本有效:

Session session = sessionFactory.openSession();
ManagedSessionContext.bind(session);
Transaction tx = session.beginTransaction();

EntityA a = daoA.read();
EntityC c = daoC.read();
a.setFoo(newFoo);
daoA.write(a);

tx.commit();
session.close(); // Each tx has its own session

session = sessionFactory.openSession(); // new session, before only a new tx
ManagedSessionContext.bind(session);
tx = session.beginTransaction();

c = daoC.read() // reads using NamedNativeQuery again. now it DOES see the write above

tx.commit();
session.close();

抱歉,示例代码比较晦涩……显然,实际的应用程序要复杂得多。我对 Hibernate 了解不多,所以我希望这是一些新手对 t运行sactions 和 session 的误解。如果事实证明这更复杂并且会有帮助,我可以尝试提取一个最小的示例来重现问题并且可以实际编译并且 运行.

问题出在 hibernate persistenceContext 缓存上,它短路了行 -> 对象转换,因为它已经看到了对象。

以下将起作用:

Session session = sessionFactory.openSession();
ManagedSessionContext.bind(session);

Transaction tx = session.beginTransaction();

EntityA a = daoA.read();
EntityC c = daoC.read();
a.setFoo(newFoo);
daoA.write(a);
session.flush();
tx.commit();

// This clears the persistenceContext cache and 
// the actionQueue so make sure everything is 
// done before this point.
session.clear(); 

Transaction tx = session.beginTransaction();
c = daoC.read();
tx.commit();

session.close();

另一种选择是使用无状态会话,不过它对批量操作更有用。