Spring JPA: контекст сохраняемости, управляемый приложением, с @Transactional и @PersistenceContext

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

Но мне интересно, могу ли я избежать отправки объекта entityManager по всей службе и методов DAO в качестве дополнительного параметра, используя инъекцию Spring @PersistenceContext и помечая методы аннотацией @Transactional, чтобы использовать транзакцию, запущенную вручную с этим менеджером объектов.

Я думаю, что могу каким-то образом справиться с этим, используя ThreadLocal для этой функции, но я буду счастлив, если смогу прикрепить это к структуре Spring.

Это пример того, что я имею в виду:


Метод действия пользовательского интерфейса:

Здесь мы видим, что транзакция запускается логикой пользовательского интерфейса, поскольку в бэкэнде нет метода фасада/команды для группировки этих вызовов с бизнес-логикой:

Long transactionid = tool.beginTransaction();

// calling business methods
tool.callBusinessLogic("purchase", "receiveGoods", 
                        paramObject1, transactionid);

tool.callBusinessLogic("inventory", "updateInventory", 
                        paramObject2, transactionid);

tool.commitTransaction(transactionid);

Внутри инструмента:

public Long beginTransaction() {
  // create the entity --> for the @PersistentContext
  Entitymanager entityManager = createEntityManagerFromFactory();
  long id = System.currentTimeMillis();
  entityManagerMap.put(id, entitymanager);

  // start the transaction --> for the @Transactional ?
  entityManager.getTransaction().begin();

  return id;
}

public void commitTransaction(Long transactionId) {
  EntityManager entityManager = entityManagerMap.get(transactionId);

  entityManager.getTransaction().commit();
}

public Object callBusinessLogic(String module, String function, 
                        Object paramObject, Long transactionid) {
    EntityManager em = entityManagerMap.get(transactionId);

    // =================================
    //        HOW TO DO THIS????
    // =================================
    putEntityManagerIntoCurrentPersistenceContext(em);

    return executeBusinessLogic(module, function, paramObject, transactionid);
}

И пример для метода службы:

public class Inventory {
  // How can i get the entityManager that's been created by the tool for this thread ?
  @PersistenceContext
  private EntityManager entityManager;

  // How can i use the transaction with that transactionId ?
  @Transactional
  public void receiveGoods(Param param) {
    // ........
  }
}

Есть ли способ добиться этого?

Спасибо !


person Albert Gan    schedule 24.02.2011    source источник


Ответы (3)


Обработка Spring аннотации @PersistenceContext делает почти именно то, что вам нужно, с одним большим отличием: вы всегда получаете EntityManager с областью действия транзакции, а Spring всегда внедряет один и тот же экземпляр для одного и того же потока, поэтому у вас есть вид распространения и не нужно беспокоиться о потокобезопасности. Но таким образом вы никогда не получите расширенный контекст!
Поверьте мне, Spring 3 и расширенный контекст персистентности плохо сочетаются друг с другом, возможно, это изменится в Spring 3.1, но я боюсь, что это не входит в их задачи. Если вы хотите использовать расширенный контекст персистентности, позвольте Spring внедрить EntityManagerFactory (через аннотацию @PersistenceUnit), а затем создайте EntityManager самостоятельно. Для распространения вам придется либо передать экземпляр в качестве параметра, либо сохранить его в ThreadLocal самостоятельно.

person Robin    schedule 06.07.2011
comment
В чем разница между созданием EntityManager вручную и внедрением с помощью @PersistenceContext. Получаю ли я другой объект диспетчера сущностей каждый раз, когда я вызываю entityManagerFactory.createEntityManager(), или просто общий диспетчер сущностей, который вводится с помощью @PersistenceContext - person JavaTechnical; 29.06.2014
comment
entityManagerFactory.createEntityManager() создает новый экземпляр управляемый приложением, а @PersistenceContext внедряет экземпляр управляемый контейнером. Проверьте спецификацию JPA на предмет разницы. В конкретном случае среды Spring экземпляр, управляемый контейнером, имеет область действия транзакции и привязан к локальному потоку, поэтому каждый поток имеет не более одного экземпляра одновременно. (поэтому нет необходимости в синхронизации) - person Robin; 30.06.2014

Действительно, чтобы иметь контекст сохраняемости, управляемый приложением, вам нужно каким-то образом «взломать» инфраструктуру @Transactional и @PersistenceContext, предоставив им свою собственную сущность. Manager и не позволяйте Spring создавать свои собственные.

Ключом к достижению этого является небольшая игра с классом TransactionSynchronizationManager, чтобы зарегистрировать свой собственный Entity Manager в локальном потоке, Spring будет использовать его для внедрения в @PersistenceContext. атрибут.

Некоторое время назад у меня возникла такая потребность для моего собственного приложения, и я разработал небольшую архитектуру на основе Spring AOP для управления расширенным контекстом сохранения.

Подробности здесь: JPA/Hibernate Global Conversation with Spring AOP

person doanduyhai    schedule 24.02.2012
comment
Хотя в настоящее время мне больше не нужна эта функция, я попробую ее, когда она мне снова понадобится в будущем. Спасибо ! - person Albert Gan; 24.02.2012

Когда вы настраиваете свои транзакции через @Transactional, вы должны передать конфигурацию своих транзакций в аннотацию.
Здесь вы начинаете свою транзакцию, а затем надеетесь, что @Transactional также будет активирован.
для получения дополнительной информации. вам лучше начать читать http://static.springsource.org/spring/docs/2.5.x/reference/transaction.html => 9.5.6. Использование @Transactional

person jelle    schedule 28.06.2011