Транзакции JavaEE - почему UserTransaction требуется только в некоторых случаях? - PullRequest
0 голосов
/ 16 апреля 2020

У нас есть приложение JavaEE, работающее в WildFly (18.0.1.final). Использование JavaEE 8 с JDK 11.

В этом приложении у нас есть несколько bean-компонентов, которые сохраняют данные в базу данных PostgreSQL, используя JPA (с hibernate). В постоянстве. xml мы определяем источник данных JTA следующим образом:

<jta-data-source>java:jboss/datasources/CustomDS</jta-data-source>

Все наши компоненты персистентности следуют одному и тому же соглашению (очень упрощенная версия generi c для Иллюстрация):

@Stateless
@Local(localinterface.class)
@Remote(remoteinterface.class)
public class GenericPersistenceBean implements localinterface, remoteinterface {
    @PersistenceContext
    private EntityManager entityManager;

    public SomeResponse persist(SomeRequest someRequres) {
      ....
      checkIfDataAlreadyPersisted(entity);
      ....
      entityManager.persist(entity);
      ....
    }

    private void checkIfDataAlreadyPersisted(SomeEntity entity) {
      CriteriaQuery ....
      .....
      entityManager.createQuery(...);
    }
}

Причина этого вопроса в том, что у нас есть один компонент персистентности (UserPersistenceBean), который делает несколько checkIfDataAlreadyPersisted по сравнению с другими. И при вызове этого UserPersistenceBean мы получаем исключение:

Причина: javax.persistence.TransactionRequiredException: WFLYJPA0060: Транзакция требуется для выполнения этой операции (либо используется транзакция, либо расширенный контекст персистентности)

При использовании UserTransaction и добавлении аннотации @TransactionManagement(TransactionManagementType.BEAN) к UserPersistenceBean исключение больше не генерируется при непосредственном вызове службы, и данные сохраняются. Однако при вызове UserPersistenceBean из другого компонента, такого как:

@Stateless
@Local(UserCreatorLocalinterface.class)
@Remote(UserCreatorRemoteinterface.class)
public class UserCreatorBean implements UserCreatorLocalinterface, UserCreatorRemoteinterface {
    @Inject
    private UserPersistenceInterfaceLocal userPersistence;

    public SomeResponse createUser(SomeRequest someRequres) {
       .....
       userPersistence.persistNewUser();
       .....
    }
}

, генерируется следующее исключение:

WFLYEJB0137: только сеансные и управляемые сообщениями компоненты с управляемым компонентом Разграничение транзакций разрешено для доступа к UserTransaction.

Я был бы очень признателен, если бы кто-нибудь мог пролить некоторый свет на:

  1. Почему UserTransaction необходим для определенного бина?
    • Это потому, что у службы больше вызовов 'entityManager.createQuery ()'?
  2. Как мы можем обработать указанное выше исключение 'WFLYEJB0137' в компоненте более высокого уровня (Например, UserCreatorBean)?
  3. Является ли эффективная практика для принудительного выполнения транзакций с помощью UserTransaction? Поскольку это не требуется для любого из других компонентов персистентности, используемых в том же сценарии ios.

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

Я пойму, если однако этого недостаточно, если кто-то хотя бы может помочь мне понять, почему UserTransaction необходима, когда компоненты выполняют около трех или более вызовов checkIfAlreadyPersisted (entityManager.createQuery(criteriaQuery)), тогда я смогу разрешить остальные.

@Stateless
@Local(UserPersistenceInterfaceLocal.class)
@Remote(UserPersistenceInterfaceRemote.class)
@TransactionManagement(TransactionManagementType.BEAN)
public class UserPersistenceBean implements UserPersistenceInterfaceLocal, UserPersistenceInterfaceRemote {
    @PersistenceContext
    private EntityManager entityManager;

    @Resource
    private UserTransaction utx;

    public SomeResponse persistNewUser(SomeRequest someRequest) {
      ....
      checkIfUserAlreadyPersisted(entity);
      ....
      checkIfUserIdentifierExist(entity); // <--- Our own generated identifier based on some business logic
      ....
      checkIfUserAccountAlreadyPersisted(entity);
      ....
      utx.begin();
      entityManager.persist(entity);
      utx.commit();
      ....
    }

    private void checkIfUserAlreadyPersisted(SomeEntity entity) {
      CriteriaQuery ....
      Predicate exists = criteriaBuilder.equal(root.get(User_.username), entity.username);
      .....
      entityManager.createQuery(...);
    }

    private void checkIfUserIdentifierExist(SomeEntity entity) {
      List<Long> userIdentifiers = getUserIdentifier(entity.getUserIdentifier());

      CriteriaQuery ....
      Join<User, UserIdentifiers> join = root.join(User_.userIdentifier);
      Expression expression = join.get(UserIdentifiers_.identifier);
      Predicate exists = expression.in(accounts);
      .....
      entityManager.createQuery(...);
    }

    private List<Long> getUserIdentifier(String userIdentifier) {
      CriteriaQuery ...
      ....
      Predicate exists = criteriaBuilder.equal(root.get(UserIdentifiers_.userIdentifier), userIdentifier);
      .....
      entityManager.createQuery(...);
    }

    private void checkIfUserAccountAlreadyPersisted(SomeEntity entity) {
      List<Long> accounts = getAccount(entity.getAccount().getAccountNumber());

      CriteriaQuery ....
      Join<User, Account> join = root.join(User_.account);
      Expression expression = join.get(Account_.identifier);
      Predicate exists = expression.in(accounts);
      .....
      entityManager.createQuery(...);
    }

    private List<Long> getAccount(String accountNumber) {
      CriteriaQuery ....
      ....
      Predicate exists = criteriaBuilder.equal(root.get(Account_.accountNumber), accountNumber);
      .....
      entityManager.createQuery(...);
    }
}
...