JMS不';不回滚XA事务(或不参与)

JMS不';不回滚XA事务(或不参与),jms,jboss5.x,jta,xa,jpa-1.0,Jms,Jboss5.x,Jta,Xa,Jpa 1.0,我对XA事务比较陌生。几天来,我一直在努力使一个简单的XA事务能够正常工作,但毫无结果 首先,我尝试使用两个不同的数据库。我设置了2个XA数据源,并在第二个数据库操作失败时成功回滚了第一个数据库操作。到目前为止,一切顺利。但后来我尝试用JMS connectionFactory替换第二个数据源,无法重现相同的行为 以下是相关代码: 数据库逻辑: @Stateless public class FirstDB implements FirstDBLocal { @PersistenceC

我对XA事务比较陌生。几天来,我一直在努力使一个简单的XA事务能够正常工作,但毫无结果

首先,我尝试使用两个不同的数据库。我设置了2个XA数据源,并在第二个数据库操作失败时成功回滚了第一个数据库操作。到目前为止,一切顺利。但后来我尝试用JMS connectionFactory替换第二个数据源,无法重现相同的行为

以下是相关代码:

数据库逻辑

@Stateless
public class FirstDB implements FirstDBLocal {

    @PersistenceContext(unitName = "xaunit")
    private EntityManager em;

    public void doSomething() {
        SomeEntity someEntity = em.find(SomeEntity.class, 1234L);
        someEntity.setSomeFlag(false);
    }

}
@Stateless
public class SecondJMS implements SecondJMSLocal {

    @Resource(mappedName = "java:/JmsXA")
    private ConnectionFactory connFactory;

    @Resource(mappedName = "queue/Some.Queue")
    private Queue q;

    @Override
    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public void sendMsg() {
        Session session = null;
        Connection conn = null;
        MessageProducer producer = null;
        try {
            conn = connFactory.createConnection("guest", "guest");

            session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);

            producer = session.createProducer(q);

            // Not sure if I need this, but I found it in the sample code
            conn.start();

            TextMessage tm = session.createTextMessage(new Date().toString());
            producer.send(tm);

            throw new RuntimeException("Fake exception");
        } catch (JMSException e) {
            e.printStackTrace();
        } catch (RuntimeException e) {
            e.printStackTrace();
        } finally {
            // close all resources
        }
    }

}
@Stateless
public class TestDBandJMS implements TestDBandJMSLocal {

    @EJB
    private FirstDBLocal firstDBLocal;

    @EJB
    private SecondJMSLocal secondJMSLocal;

    public void doStuff() {
        firstDBLocal.doSomething();
        secondJMSLocal.sendMsg();
    }

}
JMS代码

@Stateless
public class FirstDB implements FirstDBLocal {

    @PersistenceContext(unitName = "xaunit")
    private EntityManager em;

    public void doSomething() {
        SomeEntity someEntity = em.find(SomeEntity.class, 1234L);
        someEntity.setSomeFlag(false);
    }

}
@Stateless
public class SecondJMS implements SecondJMSLocal {

    @Resource(mappedName = "java:/JmsXA")
    private ConnectionFactory connFactory;

    @Resource(mappedName = "queue/Some.Queue")
    private Queue q;

    @Override
    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public void sendMsg() {
        Session session = null;
        Connection conn = null;
        MessageProducer producer = null;
        try {
            conn = connFactory.createConnection("guest", "guest");

            session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);

            producer = session.createProducer(q);

            // Not sure if I need this, but I found it in the sample code
            conn.start();

            TextMessage tm = session.createTextMessage(new Date().toString());
            producer.send(tm);

            throw new RuntimeException("Fake exception");
        } catch (JMSException e) {
            e.printStackTrace();
        } catch (RuntimeException e) {
            e.printStackTrace();
        } finally {
            // close all resources
        }
    }

}
@Stateless
public class TestDBandJMS implements TestDBandJMSLocal {

    @EJB
    private FirstDBLocal firstDBLocal;

    @EJB
    private SecondJMSLocal secondJMSLocal;

    public void doStuff() {
        firstDBLocal.doSomething();
        secondJMSLocal.sendMsg();
    }

}
胶水代码

@Stateless
public class FirstDB implements FirstDBLocal {

    @PersistenceContext(unitName = "xaunit")
    private EntityManager em;

    public void doSomething() {
        SomeEntity someEntity = em.find(SomeEntity.class, 1234L);
        someEntity.setSomeFlag(false);
    }

}
@Stateless
public class SecondJMS implements SecondJMSLocal {

    @Resource(mappedName = "java:/JmsXA")
    private ConnectionFactory connFactory;

    @Resource(mappedName = "queue/Some.Queue")
    private Queue q;

    @Override
    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public void sendMsg() {
        Session session = null;
        Connection conn = null;
        MessageProducer producer = null;
        try {
            conn = connFactory.createConnection("guest", "guest");

            session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);

            producer = session.createProducer(q);

            // Not sure if I need this, but I found it in the sample code
            conn.start();

            TextMessage tm = session.createTextMessage(new Date().toString());
            producer.send(tm);

            throw new RuntimeException("Fake exception");
        } catch (JMSException e) {
            e.printStackTrace();
        } catch (RuntimeException e) {
            e.printStackTrace();
        } finally {
            // close all resources
        }
    }

}
@Stateless
public class TestDBandJMS implements TestDBandJMSLocal {

    @EJB
    private FirstDBLocal firstDBLocal;

    @EJB
    private SecondJMSLocal secondJMSLocal;

    public void doStuff() {
        firstDBLocal.doSomething();
        secondJMSLocal.sendMsg();
    }

}
XA连接工厂配置(除注释掉的安全设置外,所有内容都是JBoss默认设置):

正如我所说,我还不太熟悉XA,但我希望在这里看到两个阶段的提交,因为应该有两个资源参与活动事务

任何帮助都将不胜感激

更新

在我尝试了@djmorton的建议后,它最终奏效了。 使用JBoss 5.1时要记住的另一件重要事情是XA JMS
ConnectionFactory
的查找名称是“java:/JmsXA”。我也试过了

@Resource(mappedName = "XAConnectionFactory")
private ConnectionFactory connFactory;

但它不起作用。

在sendMsg()方法中抛出RuntimeException后,您正在捕获它。除非将异常抛出堆栈,否则该异常不会触发事务回滚。使用容器管理的事务时,容器向方法调用添加拦截器,以设置事务,并在引发未检查的异常时处理回滚。如果异常没有从方法中抛出,那么拦截器不知道它需要将事务重新角色化

编辑1:

请注意,只有被抛出的RuntimeException或RuntimeException的子类才会导致事务回滚。选中的异常(扩展异常而不是RuntimeException的异常)将不会导致回滚,除非使用@ApplicationException(rollback=true)对其进行注释

另一种方法是注入EJBContext对象,并调用.setRollbackOnly()在方法超出范围时强制事务回滚:

@Stateless
public class SomeEjb {    
    @Resource
    private EJBContext context;

    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public void rollMeBack() {
        context.setRollbackOnly();
    }
}

EJB事务不是在任何异常发生时都标记为回滚吗?例如,在处理异常一节中明确指出,一旦抛出异常,事务被标记为回滚,即使在catch子句中也不可能持久化某个实体。ONE_PHASE_COMMIT消息在您看来是否可疑?否。只有在事务方法引发RuntimeException(不仅仅是异常)或在UserTransaction对象上调用.setRollbackOnly()时,才会回滚EJB事务。引发异常的行为并不是导致事务回滚的原因,而是将该异常传递给容器在调用EJB服务方法之前调用的事务侦听器,因此该异常实际上必须向堆栈上传递。感谢您的回复。您如何看待此“一个阶段提交”消息?这让我怀疑JMS会话不参与活动XA事务。在接受答案之前,我得再考虑一下。这个信息确实有点奇怪。您的代码显示您正在使用JmsXA连接工厂。。。是否确实使用了正确配置的XA数据源,包括正确的XA驱动程序类名?你使用的是什么版本的JBoss?然后稍微修改一下你的测试。。。不要在消息生成器中引发异常。首先调用send方法,然后调用DB modification方法。但是,向表中添加一个约束,当提交数据库更改时,该约束将失败。数据库异常应触发事务回滚。如果您看到消息被发送到MDB,那么您就知道您的消息生产者没有参与XA事务。如果消息未被发送,则表示它已成功参与事务。