【发布时间】:2014-09-25 16:51:33
【问题描述】:
我有一个持久化数据的 Web 服务,其中一种方法需要在数据库事务中。实质上是这样的:
public abstract class ControllerBase {
//this is a method that needs to run in a transaction
void batchUpsert(/* ... */) {
try {
sql.query("BEGIN").execute();
sql.query("LOCK TABLE " + tableName + " IN EXCLUSIVE MODE").execute();
//do some stuff
} finally {
sql.query("COMMIT").execute();
}
}
}
并且web服务定义在一个子类中,调用上面的方法:
@Path("symbology")
@Stateless
public class SymbologyController extends ControllerBase {
@PUT
@Path("upsert_symbology")
@Consumes(MediaType.APPLICATION_JSON)
public Response upsertSymbology(List<SymbologyRecord> symbology) {
batchUpsert(SYMBOLOGY, symbology, SYMBOLOGY.SHORT_NAME);
return Response.ok().build();
}
}
这感觉不对,因为事务部分应该由容器管理,而不是我发送 BEGIN 和 COMMIT 语句。
所以我删除了它们并在batchUpsert 方法上添加了@TransactionAttribute 注释:
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
void batchUpsert(/* ... */) {
sql.query("LOCK TABLE " + tableName + " IN EXCLUSIVE MODE").execute();
//do some stuff
}
但容器(wildfly)似乎没有创建事务,我得到一个异常:
错误:LOCK TABLE 只能在事务块中使用
我也尝试了@TransactionAttribute 注释,但也没有用。
我应该如何让 wildfly 知道它需要将我的方法包装在数据库事务中?
编辑
我刚刚意识到交易状态是活跃的:
@Resource TransactionSynchronizationRegistry tsr;
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
void batchUpsert(/* ... */) {
LOG.info("Transaction active? {}", tsr.getTransactionStatus() == Status.STATUS_ACTIVE);
//...
}
输出真。这不是应该在数据库级别运行相当于BEGIN 语句的吗?
【问题讨论】:
-
什么是
sql?它可能未注册到当前事务。您是否考虑过改用 JTA 管理的EntityManager? -
@Benjamin 我正在使用 JOOQ,
sql本质上是一个围绕DataSource的薄包装。连接由@Resource(lookup = "java:jboss/jdbc/ws") private javax.sql.DataSource postgres;提供。我从未使用过 JTA,所以不确定它会带来什么。有没有办法将数据源注册到当前事务? -
JTA 带来了事务管理,
TransactionSynchronizationRegistry是 JTA 的一部分。如果您的数据源在 wildfly 中启用了 JTA 声明:<datasource jta="true" jndi-name="java:jboss/jdbc/ws" ...>那么它应该可以工作。否则我不知道您使用的这个 JOOQ 包装器执行什么...您可以尝试直接使用数据源吗? -
@Benjamin 这就是问题所在:数据源上没有启用 jta(不知道为什么)。随时发表您的评论作为答案。
标签: jakarta-ee transactions wildfly java-ee-7