新的EntityManager有时会从MySQL获取过时的数据 [英] New EntityManager sometimes getting stale data from MySQL

查看:123
本文介绍了新的EntityManager有时会从MySQL获取过时的数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

JPA; Hibernate 4.3.6; MySQL 6.2 (InnoDB); Vaadin web application running on Tomcat

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

When I read an entity from the database I sometimes get stale data. I can’t find the pattern— sometimes the same code pulls stale data, then clean data, then stale again.

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

I am getting a new EntityManager before each query. (I was concerned that I might be somehow getting them from a pool of reused EntityManagers, but flushing the "new" EntityManager gives me an error that no transaction is in progress.)

Hibernate的调试日志和逐步执行代码,均表明每次都在访问数据库.

Hibernate's debug log, and stepping through the code, both indicate that it is hitting the database each time.

所有内容都指向经典错误:以前在事务中读取(现在已过时)数据的REPEATABLE-READ.可以肯定的是,如果我将MySQL的事务隔离设置从REPEATABLE-READ更改为READ-COMMITTED,问题就消失了.但是,如果我每次都分配一个新的EntityManager,那怎么可能?

Everything points to the classic mistake: a REPEATABLE-READ inside a transaction that had previously read the (now-stale) data. And sure enough, if I change MySQL’s transaction-isolation setting from REPEATABLE-READ to READ-COMMITTED the problem goes away. But how is that possible if I’m allocating a new EntityManager each time?

具体: 我运行下面的代码,并为templateFile字段看到正确的值"OLD".然后,我手动编辑并将更改提交到数据库中的行,并将其设置为"NEW".当我再次运行代码时,我可以看到以下任何内容:

Specifics: I run the code below and see the correct value "OLD" for the templateFile field. Then I manually edit and commit a change to the row in the database, setting it to "NEW". When I run the code again I could see any of the following:

Filename1: OLD
Filename2: OLD
OR
Filename1: OLD
Filename2: NEW
OR
Filename1: NEW
Filename2: NEW

无论哪种结果,我都会坚持",每次代码运行时都会重复执行,至少直到我弄乱了程序的其他部分为止.下次我返回测试时,结果可能会更改,也可能不会更改.

Whichever result I get tends to "stick", repeating every time the code runs, at least until I mess with other parts of the program. Next time I return to the test, the results may or may not have changed.

代码:

// 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.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连接池时,该问题似乎也消失了.持久性单元现在看起来像这样:

UPDATE: New clue. The problem also seems to go away when I switch from using a datasource defined via properties to a Tomcat connection pool. The persistence unit now looks like this:

<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联接是一个新事务.但是休眠文档状态:当您在事务内部创建实体管理器时,该实体管理器会自动加入当前事务."

For some reason I thought the implicit transaction entityManager joins was a new transaction. But Hibernate docs state: "When you create an entity manager inside a transaction, the entity manager automatically join the current transaction."

因此,显然还有另一个事务正在运行(不足为奇),并且我的提取结果会根据已读取的内容而改变(因为数据库正在REPEATABLE-READ模式下运行).

So apparently there is another transaction already running (not surprising) and my fetch results change depending on what it had already read (because the database is running in REPEATABLE-READ mode.)

短期内,我将遍历我的代码以在缺少的地方显式进行begin()事务.从长远来看,我将研究Spring Transactions,以了解如何以更加简单的方式管理事务(如评论中所建议的那样).

In the short term I will sweep through my code to explicitly begin() transactions wherever that's missing. Longer term, I will look into Spring Transactions to see about managing transactions in a more foolproof manner (as was suggested in the comments.)

这篇关于新的EntityManager有时会从MySQL获取过时的数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆