首页 > 解决方案 > JavaEE 事务 - 为什么仅在某些情况下才需要 UserTransaction?

问题描述

我们有一个在 WildFly (18.0.1.final) 中运行的 JavaEE 应用程序。将 JavaEE 8 与 JDK 11 一起使用。

在这个应用程序中,我们有一些使用 JPA(使用休眠)将数据持久化到 PostgreSQL 数据库的 bean。在 persistence.xml 中,我们定义了一个 JTA 数据源,如下所示:

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

我们的持久性 bean 都遵循相同的约定(用于说明的非常淡化的通用版本):

@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(...);
    }
}

这个问题的原因是,我们有一个持久性 bean (UserPersistenceBean),特别是它checkIfDataAlreadyPersisted比其他的多一些。当调用这个 UserPersistenceBean 我们得到一个异常:

引起:javax.persistence.TransactionRequiredException:WFLYJPA0060:执行此操作需要事务(使用事务或扩展持久性上下文)

通过使用 aUserTransaction并将@TransactionManagement(TransactionManagementType.BEAN)注解添加到 UserPersistenceBean 中,直接调用服务时不再抛出异常并且数据被持久化。但是,当从另一个 bean 调用 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:仅允许具有 bean 管理的事务划分的会话和消息驱动的 bean 访问 UserTransaction。

如果有人能说明一下,我将不胜感激:

  1. 为什么 UserTransaction 对于特定的 bean 是必需的?
    • 是因为服务有更多的“entityManager.createQuery()”调用吗?
  2. 我们如何在更高级别的 bean(例如 UserCreatorBean)中处理上述 'WFLYEJB0137' 异常?
  3. 强制使用 UserTransaction 进行交易是一种好的做法吗?因为在相同场景中使用的任何其他持久性 bean 都不需要它。

编辑

下面应该为 UserPersistenceBean 提供更多上下文。我提前道歉,我知道它非常含糊,但考虑到代码是专有的,我可以提供的尽可能多,分享更多对我来说不是很好。

我会理解它是否不够,但是,如果有人至少可以帮助我理解为什么当 bean 进行大约三个或更多 checkIfAlreadyPersisted 调用(entityManager.createQuery(criteriaQuery))时 UserTransaction 是必要的,那么我应该能够解决其余的问题。

@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(...);
    }
}

标签: hibernatejpawildflyjtajava-ee-8

解决方案


推荐阅读