New EntityManager sometimes getting stale data from MySQL

寵の児 提交于 2019-12-10 11:14:45

问题


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.

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's debug log, and stepping through the code, both indicate that it is hitting the database each time.

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?

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.

Code:

// 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>

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>

回答1:


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."

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.)

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.)



来源:https://stackoverflow.com/questions/27023935/new-entitymanager-sometimes-getting-stale-data-from-mysql

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!