休眠@NamedNativeQuery读取由实体持久写入的过时数据"normal"



我在使用Hibernate读写MySQL的Java(Dropwizard)web服务中遇到了一个有点奇怪的问题。我可以总结的最好方法是,当两个事务都在同一会话中时,在一个事务中完成的写入对于在第二个事务中执行的NamedNativeQuery来说似乎是不可见的。调试时,外部MySQL客户端可以看到写入操作。当每个事务都在自己的会话中时,所有读取都会看到一个一致的世界视图。这几乎就像写入MySQL一样,但NamedNativeQuery是从内存中的缓存版本读取的。我会更详细地解释。。。

为了描述这个问题,该应用程序有三个Hibernate实体,它们使用相同的两个数据库表,比如表X和Y以及实体A、B和C。其中两个实体(A和B)很简单,使用AbstractDAO(来自Dropwizard)的读写方法以及HQL和Hibernate Query API的方法将表中的行映射到实体。因此,表X中的一行映射到实体A的一个实例,表Y中的一行将映射到实体B的一个例子。

第三个实体(实体C)有点不同。它实际上是只读的,旨在通过连接表X和Y来收集一些聚合统计信息。它使用@NamedNativeQuery来执行单个本地MySQL查询并映射到实体中的字段。此联接使用表X上指向表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了解不多,所以我希望这是一些新手对事务和会话的误解。如果事实证明这更复杂,并且会有所帮助,我可以尝试提取一个最小的例子来重现问题,并且可以实际编译和运行。

问题是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();

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

最新更新