Подтвердить что ты не робот

Java CDI @PersistenceContext и безопасность потоков

Является ли EntityManager @Inject [ed] следующим в многоуровневых классах threadsafe?

@PersistenceContext(unitName="blah")
private EntityManager em;

Этот вопрос и этот кажется Spring конкретным. Я пользуюсь услугами JIE EE CDI

4b9b3361

Ответ 1

Хотя реализации EntityManager не являются потокобезопасными, контейнер Java EE вводит прокси-сервер, который делегирует все вызовы методов транзакционной привязке EntityManager. Поэтому каждая транзакция работает с собственным экземпляром EntityManager. Это верно для хотя бы контекста константы транзакций (по умолчанию).

Если контейнер введет новый экземпляр EntityManager в каждом bean, ниже будет работать:

@Stateless
public class Repository1 {
   @EJB
   private Repository2 rep2;

   @PersistenceContext(unitName="blah", type = PersistenceContextType.TRANSACTION)
   private EntityManager em;

   @TransactionAttribute
   public void doSomething() {
      // Do something with em
      rep2.doSomethingAgainInTheSameTransaction();
   }
}

@Stateless
public class Repository2 {
   @PersistenceContext(unitName="blah", type = PersistenceContextType.TRANSACTION)
   private EntityManager em;

   @TransactionAttribute
   public void doSomethingAgainInTheSameTransaction() {
      // Do something with em
   }
}

doSomething- > doSomethingAgainInTheSameTransaction вызов происходит в одной транзакции, и поэтому beans должен совместно использовать тот же EntityManager. Фактически они имеют один и тот же прокси EntityManager, который делегирует вызовы в тот же контекст персистентности.

Итак, вы легально используете EntityManager в singleton beans, как показано ниже:

@Singleton
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
public class Repository {
   @PersistenceContext(unitName="blah", type = PersistenceContextType.TRANSACTION)
   private EntityManager em;
}

Другим доказательством является отсутствие упоминания безопасности потоков в EntityManager javadoc. Поэтому, находясь внутри контейнера Java EE, вы не должны заботиться о concurrency доступе к EntityManager.

Ответ 2

К моему большому удивлению (после нескольких лет использования в ) EntityManager не является потокобезопасным. Это на самом деле понятно, если вы думаете об этом глубже: EntityManager - это всего лишь оболочка вокруг встроенной реализации JPA, например. сеанс в Hibernate, который по очереди представляет собой обертку . При этом EntityManager не может быть потокобезопасным, поскольку он представляет собой одно соединение/транзакцию с базой данных.

Итак, почему он работает в Spring? Потому что он переносит целевой EntityManager в прокси-сервер, в принципе используя ThreadLocal для сохранения локальной ссылки для каждого потока. Это необходимо, поскольку приложения Spring создаются поверх одиночных элементов, тогда как EJB использует пул объектов.

И как вы можете справиться с этим в своем случае? Я не знаю , но в EJB каждый сеанс без состояния и состояния bean объединяется, что означает, что вы не можете действительно вызывать метод одного и того же EJB из нескольких потоков одновременно. Таким образом, EntityManager никогда не используется одновременно. При этом инъекция EntityManager безопасна, по крайней мере, в сеансе без состояния и состояния beans.

Однако впрыскивание EntityManager в сервлеты и singleton beans не безопасно, поскольку, возможно, несколько потоков могут получить к ним доступ в одно и то же время, испорчаясь с тем же соединением JDBC.

См. также

Ответ 3

Я чувствую, что мне нужно углубиться в это, потому что мой первый ответ был не совсем прав.

Я буду ссылаться на JSR-220. В разделе 5.2 Получение EntityManager вы можете найти:

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

Хорошо, что это. Вы можете перестать читать здесь и никогда не использовать EntityManager в singleton beans, если не правильно синхронизироваться.

Но я считаю, что в спецификации есть путаница. На самом деле существуют две разные версии EntityManager. Первый - это реализация поставщика (говорящий Hibernate), который не обязан быть потокобезопасным.

С другой стороны, существует реализация контейнера EntityManager. Который также не должен быть потокобезопасным в соответствии с вышеизложенным. Но реализация контейнера действует как прокси и делегирует все вызовы реальному провайдеру EntityManager.

Так далее в спецификации в 5.9. Контракты времени выполнения между контейнером и постоянством Поставщик

Для управления контекстом постоянства транзакций, если нет EntityManager, уже связанного с транзакцией JTA: Контейнер создает новый менеджер сущностей, вызывая EntityManagerFactory.createEntityManager, когда первый вызов диспетчер объектов с Persistence-ContextType.TRANSACTION в рамках бизнес-метода, выполняемого в JTA сделка.

В свою очередь, это означает, что для каждой транзакции будет создан другой EntityManager. Код, создающий EntityManager, безопасен в соответствии с 5.3:

Методы интерфейса EntityManagerFactory являются потокобезопасными.

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

Но я не могу действительно думать о реализации сервера приложений, которая корректно работает с EntityManager, введенным в stateless beans и не корректно в пределах одиночных пакетов.

Итак, мои выводы:

  • Если вы хотите строго следовать JSR-220, никогда не используйте EntityManager в синглонах до синхронизации доступа к нему.
  • Лично я продолжу использовать EntityManager в singleton, потому что моя реализация сервера приложений отлично работает с ним. Вы можете проверить свою реализацию до этого.