2014-11-19 1 views
2

JPA; Hibernate 4.3.6; MySQL 6.2 (InnoDB); Веб-приложение Vaadin, работающее на TomcatНовый EntityManager иногда получает устаревшие данные от MySQL

Когда я прочитал сущность из базы данных, я иногда получаю устаревшие данные. Я не могу найти шаблон - иногда один и тот же код извлекает устаревшие данные, затем очищает данные, а затем снова устаревает.

Я получаю новый EntityManager перед каждым запросом. (Я был обеспокоен тем, что я мог бы каким-то образом получить их из пула повторно используемых EntityManager, но сброс «нового» 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> 

UPDATE: Новый ключ. Проблема также исчезает, когда я переключаюсь с использования источника данных, определенного через свойства, в пул соединений 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

Каковы ваши границы транзакций? – Rohit

+0

Цель состоит в том, чтобы транзакции выполнялись приложением. (Мы не используем EJB, который, я считаю, исключает возможность управления транзакциями контейнером в любом случае.) Отсюда мое предположение о том, что новая EntityManger == новая транзакция ... Если есть код или файл конфигурации, которые окончательно ответят на ваш вопрос , дайте мне знать, что вам показать ... может быть, это и есть наша проблема. – Bampfer

+0

FYI Я добавил обновление: проблема, кажется, исчезает, когда я получаю пул соединений Tomcat вместо определения источника данных через свойства в файле persistence.xml. Но я до сих пор не знаю, что с этим делать. – Bampfer

ответ

0

По какой-то причине я думал, что неявное объединение сущностей транзакции является новой транзакцией. Но Hibernate docs заявляет: «Когда вы создаете диспетчер сущности внутри транзакции, диспетчер сущности автоматически присоединяется к текущей транзакции».

Таким образом, очевидно есть другая транзакция уже запущена (не удивительно), и мои получить результаты меняются в зависимости от того, что он уже прочитал (потому что база данных работает в REPEATABLE-READ режиме.)

В краткосрочной перспективе I проведет мой код, чтобы явно начать() транзакции там, где это отсутствует. В более долгосрочной перспективе я рассмотрю «Весенние транзакции», чтобы узнать об управлении транзакциями более безопасным способом (как это было предложено в комментариях.)