【问题标题】:What annotation to use to wrap a method in a database transaction使用什么注解来包装数据库事务中的方法
【发布时间】: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();
  }
}

这感觉不对,因为事务部分应该由容器管理,而不是我发送 BEGINCOMMIT 语句。

所以我删除了它们并在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 声明:&lt;datasource jta="true" jndi-name="java:jboss/jdbc/ws" ...&gt; 那么它应该可以工作。否则我不知道您使用的这个 JOOQ 包装器执行什么...您可以尝试直接使用数据源吗?
  • @Benjamin 这就是问题所在:数据源上没有启用 jta(不知道为什么)。随时发表您的评论作为答案。

标签: jakarta-ee transactions wildfly java-ee-7


【解决方案1】:

检查您的数据源是否在 Wildfly 中启用了 JTA:

<datasource jta="true" jndi-name="java:jboss/jdbc/ws" ...>

【讨论】:

    【解决方案2】:

    我通常将我的数据库逻辑分成不同的外观。

    @Stateless
    public void DBFacade{
    
      @TransactionAttribute(REQUIRES_NEW(
      public void doSomethingIntransaction(){}
    }
    
    @Stateless
    @Path("/path")
    public class MyFrontendService{
      @EJB
      private DBFacade dbFacade;
    
      @PUT
      @Path("upsert_symbology")
      @Consumes(MediaType.APPLICATION_JSON)
      public void updateUserData(){
        dbFacade.doSomething();
      }
    
    }
    

    为什么必须这样做? 因为如果您不这样做,容器将无法拦截batchUsert() 的调用,因为您直接调用该方法。但是,如果您使用 ejb 注入,则注入的实例是实际 bean 的代理,当您在代理上调用方法时,它们的容器会拦截它,确定它是否需要活动事务,然后调用它持有的实际方法参考。这是唯一的 ejb 方式。您必须委托实际调用。

    【讨论】:

    • 我的代码按原样工作 - 问题是未在数据源上启用 jta。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多