新的EntityManager有时会从MySQL获取过时的数据

时间:2014-11-19 18:10:03

标签: java mysql hibernate jpa entitymanager

JPA; Hibernate 4.3.6; MySQL 6.2(InnoDB);在Tomcat上运行的Vaadin Web应用程序

当我从数据库中读取实体时,我有时会得到过时的数据。我找不到模式 - 有时相同的代码会提取过时的数据,然后清理数据,然后再次过时。

我在每个查询之前得到一个新的EntityManager。(我担心我可能会以某种方式从重用的EntityManagers池中获取它们,但是刷新“新”EntityManager会给我一个错误没有交易正在进行中。)

Hibernate的调试日志,并逐步执行代码,两者都表明它每次都在访问数据库。

所有内容都指向经典错误:在事先读取(现在陈旧)数据的事务中的REPEATABLE-READ。果然,如果我将MySQL的事务隔离设置从REPEATABLE-READ更改为READ-COMMITTED,问题就会消失。但是,如果我每次都分配一个新的EntityManager,那怎么可能呢?

具体细节: 我运行下面的代码,看到正确的值" OLD"用于templateFile字段。然后我手动编辑并提交对数据库中行的更改,将其设置为" 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());

的persistence.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>

1 个答案:

答案 0 :(得分:0)

出于某种原因,我认为隐式事务entityManager join是一个新事务。但Hibernate docs表示:“当您在事务中创建实体管理器时,实体管理器会自动加入当前事务。”

显然还有另一个事务已经运行(并不奇怪),我的获取结果会根据它已经读取的内容而改变(因为数据库在REPEATABLE-READ模式下运行。)

在短期内,我将扫描我的代码以明确地开始()交易,无论哪里缺少。从长远来看,我将研究Spring Transactions,以便以更加简单的方式管理交易(正如评论中所建议的那样)。