我遇到了一个使用Hibernate读取和写入MySQL的Java(Dropwizard)Web服务的一个稍微奇怪的问题。我可以总结的最好的方法是,当两个事务都在同一个会话中时,似乎在一个事务中完成的写入对于在第二个事务中执行的NamedNativeQuery是不可见的。调试时,外部MySQL客户端可以看到写入。当每个事务都在自己的会话中时,所有读取都会看到一致的世界视图。它几乎就像写入MySQL,但NamedNativeQuery正在读取内存中的缓存版本。我会尝试更详细地解释......
为了描述这个问题,app有三个Hibernate实体,它们使用相同的两个数据库表,我们只说表X和Y以及实体A,B和C.两个实体(A和B)是简单的,并使用AbstractDAO
(from Dropwizard)中的方法将表中的行映射到实体,以便读写,还有HQL和Hibernate查询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一无所知,所以我希望这是一些新手对交易和会话的误解。如果事实证明这更复杂并且有用,我可以尝试提取一个最小的例子来重现问题,并且实际上可以编译和运行。
答案 0 :(得分:1)
问题是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();
另一种选择可能是使用无状态会话,但它对批量操作更有用。