У нас есть приложение 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.
Я был бы очень признателен, если бы кто-нибудь мог пролить некоторый свет на:
- Почему UserTransaction необходим для определенного бина?
- Это потому, что у службы больше вызовов 'entityManager.createQuery ()'?
- Как мы можем обработать указанное выше исключение 'WFLYEJB0137' в компоненте более высокого уровня (Например, UserCreatorBean)?
- Является ли эффективная практика для принудительного выполнения транзакций с помощью 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(...);
}
}