Следует ли закрыть JPA Entity Manager?

83

У меня есть способ ниже.

public Profile readUser(String email){
    EntityManager em = EMF.get().createEntityManager();
    return em.find(Profile.class, email);
}

Нормально ли вышеупомянутое использование диспетчера сущностей? Или необходимо закрыть их? Любые предложения, пожалуйста.

JR Galia
источник
возможный дубликат Должен ли я close () каждый EntityManager?
rds
Нет, просто нет. Если вам не нужны утечки ...

Ответы:

131

Я полагаю, ответ таков: это зависит от обстоятельств .

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

Предположим, вы создадите диспетчер сущностей по запросу пользователя. Итак, пока вы обслуживаете данный запрос, вы будете держать свой менеджер сущностей открытым, а когда вы закончите с ним, вы его закроете.

В приложении JSE вы, возможно, считали, что хотите, чтобы диспетчер сущностей оставался открытым на протяжении всей жизни приложения (предположим, что вы не имеете дело с большими объемами данных), а затем закрываете его, когда приложение завершает работу.

Итог: когда вы его открываете и когда закрываете, полностью зависит от вашей стратегии и вашего дизайна. Вы закрываете его, когда вам больше не нужны сущности в его контексте.

В вашем примере это не очевидно, но поскольку вы создаете EM в методе, вы должны закрыть его перед возвратом, иначе у вас больше не будет к нему доступа (если вы не сохраните его в каком-то реестре, что не видно в коде).

Если вы не закроете его, ваши сущности будут оставаться прикрепленными даже после того, как вы закончите их использовать. Ваш контекст будет сохранен, даже если вы больше не сможете получить доступ к своему EM.

Спецификация JPA содержит больше деталей. В разделе 7.7 Контексты сохраняемости, управляемые приложением, говорится:

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

Все такие контексты персистентности, управляемые приложением, расширены по объему и могут охватывать несколько транзакций.

EntityManagerFactory.createEntityManagerМетод и EntityManager closeи isOpenметоды используются для управления жизненным циклом менеджера сущности приложения управляемого и связанный с ним контекст сохранения.

Расширенный контекст персистентности существует с момента создания диспетчера сущностей с использованием EntityManagerFactory.createEntityManagerдо закрытия диспетчера сущностей с помощьюEntityManager.close .

Расширенный контекст персистентности, полученный от менеджера объектов, управляемого приложением, является автономным контекстом постоянства, он не распространяется с транзакцией.

[...] EntityManager.closeМетод закрывает диспетчер сущностей, чтобы освободить его постоянный контекст и другие ресурсы. После вызова close приложение не должно вызывать какие-либо другие методы EntityManagerэкземпляра, кроме getTransactionи isOpen, в IllegalStateExceptionпротивном случае будет брошен вызов . Если метод close вызывается, когда транзакция активна, контекст сохранения остается управляемым до завершения транзакции.

EntityManager.isOpenМетод показывает, открыт ли управляющий объектом. isOpenМетод возвращает истину , пока диспетчер объектов не был закрыт. Чтобы действительно понять, как это работает, важно понять отношения между менеджером сущностей и контекстом.

Итак, как вы можете видеть, диспетчер сущностей - это общедоступный интерфейс, через который вы получаете доступ к своим сущностям, однако ваши сущности находятся в контексте, прикрепленном к вашему диспетчеру сущностей. Понимание жизненного цикла различных типов контекстов ответит на ваш вопрос.

Контексты сохраняемости могут быть разных типов. В приложениях Java EE вы можете иметь либо контекст персистентности в области транзакции, либо контекст расширенного сохранения . В приложении JSE характер контекста контролируется разработчиком. .

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

В рамках транзакции

В приложении Java EE, использующем контекст персистентности в области транзакции, когда вы впервые обращаетесь к своему менеджеру сущностей, он проверяет, имеет ли текущая транзакция JTA присоединенный контекст, если контекст еще не присутствует, создается новый контекст и связывается менеджер сущностей. в этом контексте. Затем объект считывается из базы данных (o из кеша, если он есть), и помещается в контекст. Когда ваша транзакция завершается (фиксация или откат), контекст становится недействительным и все объекты в нем отсоединяются. Это классический сценарий для сессионных bean-компонентов без сохранения состояния.

@PersistenceContext(unitName="EmplService")
EntityManager em;

Это также означает, что в зависимости от того, как вы разрабатываете свои транзакции, вы можете получить более одного контекста.

Контекст с расширенным постоянством

В приложении Java EE с сессионными bean-компонентами с отслеживанием состояния вам может потребоваться, чтобы контекст выдерживал многократные вызовы bean-компонентов, поскольку вам не нравится фиксировать, пока bean-компонент не будет отмечен для удаления, верно? В этих случаях вам нужно использовать расширенный контекст сохранения. В этом случае контекст сохранения создается, когда он впервые нужен, но он не станет недействительным, пока вы не отметите компонент с сохранением состояния для удаления.

@PersistenceContext(unitName="EmplService", type=PersistenceContextType.EXTENDED)

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

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

Управляемый приложением

Вы всегда можете вручную создать экземпляр фабрики диспетчера сущностей и диспетчера сущностей. Это то, что вы обычно делаете в приложении JSE, верно?

Для такого рода приложений у вас обычно нет контейнера для обработки транзакций JTA, верно? Таким образом, вы используете локальные для ресурсов транзакции и несете ответственность за ручную фиксацию или откат изменений.

Для этого типа приложений, когда вы создаете экземпляр своего менеджера сущностей, к нему автоматически присоединяется контекст.

В зависимости от вашего приложения вы можете решить создать глобальный менеджер сущностей, жизненный цикл которого привязан к жизни самого приложения. Это единый менеджер сущностей на все время существования приложения. В этом случае ваш контекст будет создан и уничтожен вашим менеджером сущностей.

Или вы можете создать диспетчер сущностей для каждого разговора (т. Е. Транзакции) с пользователем вашего приложения. В этом случае объем определяется вами, но все же ваш контекст будет создан и уничтожен вашим менеджером сущностей.

Эдвин Далорцо
источник
Отличный ответ, но мне нужно знать: открытие и закрытие EntityManager несколько раз во время сеанса имеет высокую производительность? Создавать и закрывать только один раз или создавать / использовать / закрывать его в каждой операции с базой данных, что является лучшим подходом? "это зависит" нормально, но должно иметь более подходящее применение для большинства случаев использования ..
tomrlh
4
@tomurlh Насколько я понимаю, стоимость создания EntityManagerдолжна быть незначительной. С моей точки зрения EntityManager - это просто абстракция для работы с единицей работы текущей транзакции. Я считаю, что создавать и уничтожать по одному за транзакцию - это нормально. Теперь это имеет другие последствия, потому что EntityManagerсерверы в качестве кеша транзакций для ваших сущностей и, следовательно, с четко определенной областью транзакции и правильным взаимодействием с сущностями могут использовать преимущества этого кеша.
Эдвин Далорцо 01
Метод EntityManager.close закрывает диспетчер сущностей, чтобы освободить его контекст постоянства. Что такое контекст постоянства?
gstackoverflow
Это также означает, что в зависимости от того, как вы разрабатываете свои транзакции, вы можете получить более одного контекста. Не могли бы вы объяснить как?
gstackoverflow