联合会; hibernate 4.3.6; MySQL 6.2(InnoDB);在 Tomcat 上运行的 Vaadin Web 应用程序
当我从数据库中读取一个实体时,我有时会得到过时的数据。我找不到模式 - 有时相同的代码会提取陈旧数据,然后清理数据,然后再次陈旧。
我在每次查询之前都得到一个新的 EntityManager。(我担心我可能会以某种方式从重复使用的 EntityManager 池中获取它们,但是刷新"new"EntityManager 给我一个错误没有正在进行的交易。)
Hibernate 的调试日志和单步执行代码,都表明它每次都在访问数据库。
一切都指向经典错误:事务中的 REPEATABLE-READ 先前已读取(现在过时的)数据。果然,如果我将 MySQL 的事务隔离设置从 REPEATABLE-READ 更改为 READ-COMMITTED,问题就会消失。但是,如果我每次都分配一个新的 EntityManager,这怎么可能呢?
细节: 我运行下面的代码并看到 templateFile 字段的正确值“OLD”。然后我手动编辑并提交对数据库中行的更改,将其设置为“NEW”。当我再次运行代码时,我可以看到以下任何内容:
Filename1: OLD
Filename2: OLD
OR
Filename1: OLD
Filename2: NEW
OR
Filename1: NEW
Filename2: NEW
我得到的任何结果都倾向于“坚持”,每次代码运行时都会重复,至少直到我弄乱了程序的其他部分。下次我返回测试时,结果可能会或可能不会改变。
代码:
// the factory is a static instance, initialized once
EntityManagerFactory myFactory = Persistence.createEntityManagerFactory(“foo.entities.persistence-unit”);
// . . .
// The actual test:
EntityManager entityManager1 = myFactory.getNewEntityManager();
Household household1 = entityManager1.find(Household.class, 7);
entityManager1.refresh(household1);
System.out.println("Filename1: " + household1.getTemplateFile());
// read second copy of same entity
EntityManager entityManager2 = myFactory.getNewEntityManager();
Household household2 = entityManager2.find(Household.class, 7);
entityManager2.refresh(household2);
System.out.println("Filename2: " + household2.getTemplateFile());
持久性.xml:
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
version="2.1">
<persistence-unit name=“foo.entities.persistence-unit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/myschema"></property>
<property name="javax.persistence.jdbc.user" value="mydba"></property>
<property name="javax.persistence.jdbc.password" value=“********”></property>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"></property>
<property name="javax.persistence.schema-generation.create-database-schemas" value="false"></property>
</properties>
</persistence-unit>
</persistence>
更新: 新线索。当我从使用通过属性定义的数据源切换到 Tomcat 连接池时,问题似乎也消失了。持久化单元现在看起来像这样:
<persistence-unit name="foo.entities.persistence-unit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<non-jta-data-source>java:comp/env/jdbc/foo</non-jta-data-source>
</persistence-unit>
最佳答案
出于某种原因,我认为隐式事务 entityManager join 是一个新事务。但是Hibernate docs状态:“当您在事务中创建实体管理器时,实体管理器会自动加入当前事务。”
所以显然有另一个事务已经在运行(不足为奇)并且我的获取结果会根据它已经读取的内容而变化(因为数据库正在以可重复读取模式运行。)
在短期内,我将遍历我的代码,以在缺少的地方显式开始()事务。从长远来看,我将研究 Spring Transactions,以了解如何以更简单的方式管理事务(如评论中所建议的那样。)
关于java - 新的 EntityManager 有时会从 MySQL 获取陈旧数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27023935/