Java:多线程中的XA事务传播

5

如何在Java SE(而非Java EE或Spring)中使用事务管理器(例如BitronixJBoss TSAtomikos)来支持以下用例:

假设我们有以下类:

public class Dao {

    public void updateDatabase(DB db) {
        connet to db
        run a sql
    }

}

然后我们从中创建一个Java Runnable,如下所示:

public class MyRunnable extends Runnable {

    Dao dao;
    DB db;

    public MyRunnable(Dao dao, DB db) {
        this.dao=dao;
        this.db = db;
    }           

    public run() throws Exception {
        return dao.updateDatabase(db);
    }
}

现在在我们的服务层中,我们有另一个类:

public class Service {

    public void updateDatabases() {

        BEGIN TRANSACTION;

        ExecutorService es = Executors.newFixedThreadPool(10);

        ExecutorCompletionService ecs = new ExecutorCompletionService(es);

        List<Future<T>> futures = new ArrayList<Future<T>>(n);

        Dao dao = new Dao();

        futures.add(ecs.submit(new MyRunnable(dao, new DB("db1")));
        futures.add(ecs.submit(new MyRunnable(dao, new DB("db2")));
        futures.add(ecs.submit(new MyRunnable(dao, new DB("db3")));

        for (int i = 0; i < n; ++i) {
            completionService.take().get();
        }

       END TRANSACTION;
    }

}

客户端可以是Servlet或任何其他多线程环境:

public MyServlet extend HttpServlet {

    protected void service(final HttpServletRequest request, final HttpServletResponse response) throws IOException {

        Service service = new Service();

        service.updateDatabases();

    }

}

BEGIN TRANSACTION 和 END TRANSACTION 的正确代码是什么?这可行吗?如果不行,需要做哪些改变?要求保持 updateDatabases() 方法并发(因为它将同时访问多个数据库)和事务性。

3个回答

4
似乎可以使用AtomikosSubTxThread来完成此操作。
//first start a tx
TransactionManager tm = ...
tm.begin();

Waiter waiter = new Waiter();

//the code that calls the first EIS; defined by you
SubTxCode code1 = ...

//the associated thread
SubTxThread thread1 = new SubTxThread ( waiter , code1 );

//the code that calls the second EIS; defined by you
SubTxCode code2 = ...

//the associated thread
SubTxThread thread2 = new SubTxThread ( waiter , code2 );

//start each thread
thread1.start();

thread2.start();

//wait for completion of all calls
waiter.waitForAll();

//check result
if ( waiter.getAbortCount() == 0 ) {
    //no failures -> commit tx
    tm.commit();
} else {
    tm.rollback();
}

1

XA规范要求所有XA调用都在同一线程上下文中执行。为了阐述这一点的原因,是因为提交可能在您的线程中的任何事务分支甚至创建之前就被调用。

如果您只对如何在JBoss TS中执行这三个调用的XA事务感兴趣

首先确保您的-ds.xml将您的数据源指定为<xa-datasource>

InitialContext ctx = new InitialContext(parms);
UserTransaction ut = (UserTransaction) ctx.lookup("java:comp/UserTransaction");

ut.begin();

//Some Transactional Code

ut.commit();

请注意,使用上述代码将无法使用ExecutorService并行调用。

附带说明:我不太了解它,但JTS / OTS声称允许多个线程在事务中共享。我认为它通过传播类似于ws-coordination / ws-transaction的事务上下文来实现,并由JBossTS支持。可能是一个错误的线索,但如果您没有时间压力,研究一下可能会很值得。


EJB是否会保持事务性,即使bean生成线程,就像这里一样?由于J2EE规范实际上禁止除资源适配器以外的组件生成线程,我非常怀疑。 - Tom Anderson
1
抱歉,你是对的,这段内容来自 JBoss TS 文档 - XA 规范本身要求整个事务在单个线程中执行。“X/Open XA 接口规定与事务关联的 XA 调用必须从同一线程上下文中调用。” - nsfyn55

0

你怎么样?

  1. BEGIN_TRANSATION:连接到您的服务中的所有3个数据库,
  2. 将连接对象(而不是db对象)传递给MyRunnable
  3. END_TRANSACTION:在您的服务中调用所有3个连接上的提交和关闭

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接