Я боролся с этой проблемой в течение нескольких дней, и я не могу найти решение, которым я доволен. Я, вероятно, могу обойти это через различные степени косвенности, но это похоже на то, что я должен быть в состоянии сделать, и хотя я нашел несколько других людей, которые могут иметь мою проблему, ни один из них, кажется, не имеет точного проблема или нет ответов.
Несмотря на то, что раньше я успешно делал это с помощью Hibernate, я не смог заставить его работать с JPA, на чем я сейчас и концентрируюсь.
Предварительная информация:
- Приложение Guice 3.0, использующее постоянное расширение, спящий режим (с аннотациями) и c3p0 (до 5 соединений). Мы внедряем EntityManager от провайдера, и я могу подтвердить, что объект EntityManager остается тем же, что и объект транзакции в течение всего этого процесса.
- Разработанные как часть веб-приложения, текущие проблемы возникают только в автоматизированных интеграционных тестах, использующих JUnit и dbUnit. Доступ осуществляется к одноэлементному объекту (.in (Scopes.SINGLETON)), но этот объект Singleton зависит только от инжектора и @Transactional для обеспечения безопасности потока.
- В базе данных есть таблица файлов, которая сопоставлена с аннотированным FileContainer, и таблица mimetype, которая сопоставлена с объектом MIMEType. В приложении при создании нового файла мы сначала извлекаем объект MIMEType как NamedQuery и сохраняем его.
- База данных PostgreSQL 8.4 с использованием postgresql-8.4-702.jdbc3.jar.
- Система умеренно требовательна к обстоятельствам, при которых происходит сбой: мне нужно создать отдельный поток и выполнить оттуда доступ.
Отображение для поля mimetype в объекте FileContainer:
@ManyToOne(fetch=FetchType.EAGER, cascade={})
@JoinColumn(name="mimetype_id", referencedColumnName="id", nullable=false, updatable=true)
Объект MIMEType аннотируется следующим образом:
@Entity
@Table(name="mimetypes")
@org.hibernate.annotations.Immutable
@Cacheable(true)
@NamedQuery(name="mt.ext", query="from MIMEType where extension = :ext",
hints= {@QueryHint(name="org.hibernate.fetchSize", value="1"),
@QueryHint(name="org.hibernate.readOnly", value="true")})
Все вышеприведенные аннотации являются версиями javax.persistence.
Метод addFile
помечен @com.google.inject.persist.Transactional
. Изменение этого параметра на использование введенного EntityManager в классе не влияет на результат.
Процесс создания объекта выглядит следующим образом:
- Возьмите EntityManager и FileDAO из инжектора. Они оба приходят от поставщика.
- Получите объект MIMEType, который мы будем использовать, из базы данных, вызвав em.createNamedQuery ("mt.ext", MIMEType.class) .setParameter ("ext", extension) .getSingleResult ();
- Создайте объект
FileContainer
и заполните его, включая вызов `setMimetype (MIMEType)` объектом, который мы только что получили
- Call
dao.save(fileContainer)
, который устанавливает базовое поле в FileContainer
объекте, а затем вызывает em.persist(Object)
. Извлечение этого из DAO и использование EntityManager
, введенного ранее, не меняет результат.
В этот момент, как только EntityManager.flush()
происходит (где бы это ни происходило - в конце транзакции или перед ней), происходит ВСТАВКА, и код блокируется.
Когда я проверяю pg_stat_activity
и сравниваю его с pg_locks
, я вижу, что оператор вставки блокируется соединением «Idle in транзакция» и находится в состоянии ожидания. Удаление вставки MIMEType (и установка для столбца разрешения пустых значений) позволяет коду работать нормально, а проверка значений MIMEType указывает на то, что он был получен правильно.
Любые мысли или идеи приветствуются.
EDIT:
Это иерархия, надеюсь, она проясняет порядок операций и что происходит:
- Test Harness (Инициализирует инжектор и использует бегун для ввода его в определенные тестовые классы)
- Контрольный пример
- Тема начата
- Ввести транзакционный блок на одноэлементном объекте (инициализированном тем же самым инжектором) из потока, подтвердив, что
EntityManager
объекты идентичны во всех объектах DAO и где происходит ошибка.
EntityManager
объект последовательно получается с помощью вызова injector.getInstance(EntityManager.class)
или путем получения объекта Provider
.
- Пройдите описанный выше процесс, все в одной теме.
- Стоп
flush()
Кажется, что в коде нет ничего, что должно быть локальным для потока, которое распределяется между потоками, и все инициализируется с помощью инжектора, что меня озадачивает.