JPA: Всегда ли EntityManager.find () возвращает одну и ту же ссылку на объект для одного и того же ключа?

У меня есть интеграционный тест DAO, в котором я использую общий EntityManager (через Spring, используя SharedEntityManagerCreator). Тестовый класс помечен как @Transactional, как и тестируемый метод DAO.

И в тестовом классе, и в DAO я получаю объект User следующим образом:

User user = em.find(User.class, "test");

В настройке моего теста я изменил объект пользователя, но я не видел изменений в DAO, когда тест начинался. Оказалось, что две ссылки не относятся к одному и тому же объекту; Я доказал это в своем тестовом классе, используя:

System.out.println("User objects equal = " + (user == dao.getUser()));

Это распечатано ложно. Я ожидал, что каждый вызов EntityManager с использованием одного и того же ключа будет возвращать одну и ту же ссылку на объект, и был удивлен (и немного встревожен!), Обнаружив, что это не так. Может ли кто-нибудь пролить свет на это? Я реорганизовал свой код, так что на самом деле это не проблема (в DAO все равно не должно было быть объекта User), но я все же хотел бы понять это лучше.

Спасибо!

Java 1.6u22, Toplink Essentials 2.0.1, Spring 2.5.6


person Conan    schedule 19.11.2010    source источник


Ответы (2)


find() возвращает тот же экземпляр в пределах контекста постоянства.

В случае общего EntityManager (контекст персистентности в области видимости транзакции, управляемой контейнером, в терминах спецификации JPA) жизненный цикл контекста постоянства привязан к транзакции, поэтому find() возвращает тот же экземпляр при вызове из той же транзакции. Я полагаю, что в вашем случае настройка вашего теста не происходит в той же транзакции, что и метод тестирования, поэтому find() создает разные экземпляры.

person axtavt    schedule 19.11.2010
comment
axtavt правильный. Когда EntityManager закрыт, связанный с ним контекст сохраняемости собирается сборщиком мусора. Если ваш экземпляр EntityManager автоматически предоставляется контейнером вместе с транзакцией, тогда ваши вызовы em.find () всегда будут возвращать один и тот же физический объект для одного и того же первичного ключа, пока та же транзакция (и EntityManager) все еще остаются открыть. Если вам нужен EntityManager с более длительным сроком службы, у вас есть возможность использовать EntityManager, управляемый приложением, а не управляемый контейнером, но с некоторой дополнительной работой. - person Jim Tough; 20.11.2010
comment
Понятно, спасибо за ответы. Я уверен, что и мой тест, и мой dao разделяют только одну транзакцию, поскольку методы, аннотированные @Before и @After, включены в аннотацию @Transactional; Журналы Toplink также показывают одну транзакцию. Итак, я думаю, я не должен получать один и тот же EntityManager как в тестовом классе, так и в DAO. Есть ли способ узнать, одинаковы ли два EntityManager? Я знаю, что общий EntityManager, который предоставляет мне Spring, на самом деле является прокси-сервером, который делегирует вызовы, поэтому я не вижу очевидного способа проверки. - person Conan; 22.11.2010
comment
@Conan: Я проверил и обнаружил, что методы _1 _ / _ 2_ фактически выполняются в той же транзакции, что и @Test, поэтому em.find() возвращает те же экземпляры. Возможно, у вашей проблемы другая причина. Вы можете войти TransactionSynchronizationManager.getCurrentTransactionName(), чтобы проверить идентичность транзакции. - person axtavt; 22.11.2010
comment
@Conan: Также я предполагаю, что ваши тесты поддерживают Spring, как описано здесь static.springsource.org/spring/docs/2.5.x/reference/. Иначе @Transactional в тестовых классах не даст никакого эффекта. - person axtavt; 22.11.2010
comment
@avtaxt: Да, мои тесты поддерживают Spring, я внимательно изучал результаты отладки. Спасибо за ваше предложение, я не знал, что можно было получить транзакцию таким образом; Я попробую и посмотрю, смогу ли я почерпнуть дополнительную информацию. У меня есть подозрение, что SharedEntityManagerCreator может не подобрать существующий EntityManager с привязкой к потоку (возможно, это другой поток по какой-то причине) и поэтому возвращает новый, и в этом случае я бы понял, что ссылки на мои объекты User не соответствуют , поскольку они исходят из разных контекстов существования. - person Conan; 24.11.2010

Нет. В любом случае вам следует полагаться на объект РАВЕНСТВО, а не на ИДЕНТИЧНОСТЬ. Переопределите метод equals.

person willcodejavaforfood    schedule 19.11.2010