hibernate - 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。
如果有人能说明一下,我将不胜感激:
- 为什么 UserTransaction 对于特定的 bean 是必需的?
- 是因为服务有更多的“entityManager.createQuery()”调用吗?
- 我们如何在更高级别的 bean(例如 UserCreatorBean)中处理上述 'WFLYEJB0137' 异常?
- 强制使用 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(...);
}
}
解决方案
推荐阅读
- javascript - 通过删除字符串来计算数组的总和?
- sqlalchemy - SQLAlchemy:如何将字符串转换为日期,它应该是正确的日期格式
- javascript - 如果已经添加到其中的产品从后端删除,如何确保我的前端 Vue.js 商品购物车没有过期?
- macos - MacOS Big Sur Apache Server Redis 类未找到
- python - 使用 Gstreamer 管道几秒钟后,麦克风停止向扬声器发送语音
- javascript - 访问 React 组件中定义的变量或外部脚本
- python - 数据帧中的最小-最大归一化
- python-3.x - 将 Entry 小部件中的文本值存储到变量时出现意外输出
- python - Python中的多进程执行器
- python - 如果一个值重复,如何取熊猫列的平均值?