用于在数据库事务中包装方法的注释

时间:2014-09-25 16:51:33

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

我有一个持久保存数据的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语句。

所以我删除了它们并在@TransactionAttribute方法上添加了batchUpsert注释:

@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);
  //...
}

输出true。这不应该在数据库级别运行等效的BEGIN语句吗?

2 个答案:

答案 0 :(得分:1)

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

@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方式。您必须委派实际的调用。

答案 1 :(得分:1)

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

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