【问题标题】:New EntityManager sometimes getting stale data from MySQL新的 EntityManager 有时会从 MySQL 获取陈旧的数据
【发布时间】:2015-01-17 09:21:16
【问题描述】:

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

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

在每次查询之前我都会得到一个新的 EntityManager。(我担心我可能会以某种方式从重复使用的 EntityManager 池中获取它们,但是刷新“新”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());

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>

【问题讨论】:

  • 你的交易边界是什么?
  • 目的是事务由应用程序管理。 (我们没有使用 EJB,我认为这排除了让我们的事务由容器管理的可能性。)因此我假设一个新的 EntityManger == 一个新的事务......如果有代码或配置文件可以明确回答你的问题,让我知道要向您展示什么——也许这就是我们的问题所在。
  • 仅供参考我添加了一个更新:当我使用 Tomcat 连接池而不是通过 persistence.xml 中的属性定义数据源时,问题似乎消失了。但我仍然不知道该怎么做。
  • 我认为实体管理器需要明确定义何时打开/关闭事务,例如:em.getTransaction().beginTransaction(); ...做一些事情.. em.getTransaction().commit()。也就是说,请查看春季交易以管理此问题。 Bean Managed 交易经常以流泪和怪异行为告终,没有人能够解决(就像您最初的问题一样)。
  • @Bampfer 您的假设在应用程序管理的EntityManager 中是正确的。但是,在您第二次读取实体之前,您是close()clear() 第一个EntityManager 实例吗?

标签: java mysql hibernate jpa entitymanager


【解决方案1】:

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

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

在短期内,我将扫描我的代码以明确地 begin() 交易,无论哪里丢失。从长远来看,我将研究 Spring Transactions 以了解以更简单的方式管理事务(正如 cmets 中所建议的那样。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-22
    • 2018-12-15
    • 2011-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多