【发布时间】: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