title: JavaEE transactions - why is UserTransaction only required in some cases?
body: <p>We have a JavaEE application running in WildFly (18.0.1.final). Using JavaEE 8 with JDK 11.</p>
  
  <p>In this application we have a few beans that persists data to a PostgreSQL database, using JPA (with hibernate). In the persistence.xml we define a JTA data source as follow:</p>
  
  <p><code>&lt;jta-data-source&gt;java:jboss/datasources/CustomDS&lt;/jta-data-source&gt;</code>  </p>
  
  <p>Our persistence beans all follow the same convention (very watered-down generic version for illustration):</p>
  
  <pre><code>@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(...);
      }
  }
  </code></pre>
  
  <p>The reason for this question is, we have one persistence bean (UserPersistenceBean) in particular that does a few more <code>checkIfDataAlreadyPersisted</code> compared to the others. And when invoking this UserPersistenceBean we get an exception:</p>
  
  <blockquote>
    <p>Caused by: javax.persistence.TransactionRequiredException: WFLYJPA0060: Transaction is required to perform this operation (either use a transaction or extended persistence context)</p>
  </blockquote>
  
  <p>By using a <code>UserTransaction</code> and adding the <code>@TransactionManagement(TransactionManagementType.BEAN)</code> annotation to the UserPersistenceBean, the exception is no longer thrown when invoking the service directly and the data gets persisted. However, when invoking the UserPersistenceBean from another bean such as:</p>
  
  <pre><code>@Stateless
  @Local(UserCreatorLocalinterface.class)
  @Remote(UserCreatorRemoteinterface.class)
  public class UserCreatorBean implements UserCreatorLocalinterface, UserCreatorRemoteinterface {
      @Inject
      private UserPersistenceInterfaceLocal userPersistence;
  
      public SomeResponse createUser(SomeRequest someRequres) {
         .....
         userPersistence.persistNewUser();
         .....
      }
  }
  </code></pre>
  
  <p>then the following exception is thrown:</p>
  
  <blockquote>
    <p>WFLYEJB0137: Only session and message-driven beans with bean-managed transaction demarcation are allowed to access UserTransaction.</p>
  </blockquote>
  
  <p>I would really appreciate if someone could please shed some light on:</p>
  
  <ol>
  <li>Why the UserTransaction is necessary for a particular bean? 
  
  <ul>
  <li>Is it because the service has more 'entityManager.createQuery()' calls?</li>
  </ul></li>
  <li>How can we handle the above 'WFLYEJB0137' exception in the higher-level bean (UserCreatorBean for example)?</li>
  <li>Is it good practice either way to force the transactions with UserTransaction? As it is not required for any of the other persistence beans used in the same scenarios.</li>
  </ol>
  
  <hr>
  
  <p><strong>Edit</strong> <br/> <br/>
  Below should give some more context to the UserPersistenceBean. I apologize in advance, I know that it is very vague but it is as much as I can provide considering the code is proprietary and it won't be very good for me to share more.</p>
  
  <p>I will understand if it's insufficient, however, if someone could at least help me to understand why UserTransaction is necessary when the beans does around three or more checkIfAlreadyPersisted calls (<code>entityManager.createQuery(criteriaQuery)</code>) then I should be able to resolve the rest.</p>
  
  <pre><code>@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); // &lt;--- 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&lt;Long&gt; userIdentifiers = getUserIdentifier(entity.getUserIdentifier());
  
        CriteriaQuery ....
        Join&lt;User, UserIdentifiers&gt; join = root.join(User_.userIdentifier);
        Expression expression = join.get(UserIdentifiers_.identifier);
        Predicate exists = expression.in(accounts);
        .....
        entityManager.createQuery(...);
      }
  
      private List&lt;Long&gt; getUserIdentifier(String userIdentifier) {
        CriteriaQuery ...
        ....
        Predicate exists = criteriaBuilder.equal(root.get(UserIdentifiers_.userIdentifier), userIdentifier);
        .....
        entityManager.createQuery(...);
      }
  
      private void checkIfUserAccountAlreadyPersisted(SomeEntity entity) {
        List&lt;Long&gt; accounts = getAccount(entity.getAccount().getAccountNumber());
  
        CriteriaQuery ....
        Join&lt;User, Account&gt; join = root.join(User_.account);
        Expression expression = join.get(Account_.identifier);
        Predicate exists = expression.in(accounts);
        .....
        entityManager.createQuery(...);
      }
  
      private List&lt;Long&gt; getAccount(String accountNumber) {
        CriteriaQuery ....
        ....
        Predicate exists = criteriaBuilder.equal(root.get(Account_.accountNumber), accountNumber);
        .....
        entityManager.createQuery(...);
      }
  }
  </code></pre>