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

时间:2021-02-11 20:59:01

How can I use a transaction manager (such as Bitronix, JBoss TS or Atomikos) in a Java SE (not Java EE or Spring) to support the following use case:

如何在Java SE(非Java EE或Spring)中使用事务管理器(如Bitronix,JBoss TS或Atomikos)来支持以下用例:

Let's assume we have the following class:

我们假设我们有以下类:

public class Dao {

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

}

and we create a Java Runnable from that, like the following:

我们从中创建一个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);
    }
}

Now in our Service layer, we have another class:

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

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;
    }

}

And the client can be a Servlet or any other multi-threaded environment:

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

public MyServlet extend HttpServlet {

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

        Service service = new Service();

        service.updateDatabases();

    }

}

What would be the correct code for BEGIN TRANSACTION and END TRANSACTION parts? Is this even feasible? If not, what needs to be changed? The requirements is to keep the updateDatabases() method concurrent (since it will be accessing multiple databases at the same time) and transactional.

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

3 个解决方案

#1


2  

Seems like this can be done using Atomikos using SubTxThread

这似乎可以使用SubTxThread使用Atomikos完成

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

#2


1  

XA Specification mandates that all XA calls be executed in the same thread context. To elaborate on the reason for this its because the commit could be called before any of the transactional branches are even created in your threads.

XA规范要求所有XA调用都在同一个线程上下文中执行。详细说明其原因,因为可以在任何事务分支甚至在您的线程中创建之前调用提交。

if you are just interested in how to execute those three calls in a XA transaction in JBoss TS

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

First make sure your -ds.xml specifies your datasource as an <xa-datasource>

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

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

ut.begin();

//Some Transactional Code

ut.commit();

Keep in mind with the code above you would not be able to use the ExecutorService to parallelize the calls.

请记住上面的代码,您将无法使用ExecutorService来并行化调用。

Side Note: I don't know a lot about it but JTS/OTS claims to allow multiple threads to share in a transaction. I think it does this by propagating transactional context similar to ws-coordination/ws-transaction and is supported by JBossTS. Could be a red herring, but if your not under a time crunch it might be worth researching.

附注:我不太了解它,但JTS / OTS声称允许多个线程在事务*享。我认为它是通过传播类似于ws-coordination / ws-transaction的事务上下文来实现的,并且得到了JBossTS的支持。可能是一个红鲱鱼,但如果你没有时间紧缩,它可能值得研究。

#3


0  

How about you

你呢

  1. BEGIN_TRANSATION: Connect to all 3 databases in your Service,
  2. BEGIN_TRANSATION:连接到您服务中的所有3个数据库,

  3. pass the Connection objects (instead of db object) to MyRunnable
  4. 将Connection对象(而不是db对象)传递给MyRunnable

  5. END_TRANSACTION: invoke commit and close on all 3 connections in your Service
  6. END_TRANSACTION:调用提交并关闭服务中的所有3个连接

#1


2  

Seems like this can be done using Atomikos using SubTxThread

这似乎可以使用SubTxThread使用Atomikos完成

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

#2


1  

XA Specification mandates that all XA calls be executed in the same thread context. To elaborate on the reason for this its because the commit could be called before any of the transactional branches are even created in your threads.

XA规范要求所有XA调用都在同一个线程上下文中执行。详细说明其原因,因为可以在任何事务分支甚至在您的线程中创建之前调用提交。

if you are just interested in how to execute those three calls in a XA transaction in JBoss TS

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

First make sure your -ds.xml specifies your datasource as an <xa-datasource>

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

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

ut.begin();

//Some Transactional Code

ut.commit();

Keep in mind with the code above you would not be able to use the ExecutorService to parallelize the calls.

请记住上面的代码,您将无法使用ExecutorService来并行化调用。

Side Note: I don't know a lot about it but JTS/OTS claims to allow multiple threads to share in a transaction. I think it does this by propagating transactional context similar to ws-coordination/ws-transaction and is supported by JBossTS. Could be a red herring, but if your not under a time crunch it might be worth researching.

附注:我不太了解它,但JTS / OTS声称允许多个线程在事务*享。我认为它是通过传播类似于ws-coordination / ws-transaction的事务上下文来实现的,并且得到了JBossTS的支持。可能是一个红鲱鱼,但如果你没有时间紧缩,它可能值得研究。

#3


0  

How about you

你呢

  1. BEGIN_TRANSATION: Connect to all 3 databases in your Service,
  2. BEGIN_TRANSATION:连接到您服务中的所有3个数据库,

  3. pass the Connection objects (instead of db object) to MyRunnable
  4. 将Connection对象(而不是db对象)传递给MyRunnable

  5. END_TRANSACTION: invoke commit and close on all 3 connections in your Service
  6. END_TRANSACTION:调用提交并关闭服务中的所有3个连接