JMS不回滚XA事务(或不参与一个)

时间:2014-03-22 16:49:51

标签: jms jboss5.x jta xa jpa-1.0

我对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);
    }

}

JMS代码

@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默认设置):

<tx-connection-factory>
      <jndi-name>JmsXA</jndi-name>
      <xa-transaction/>
      <rar-name>jms-ra.rar</rar-name>
      <connection-definition>org.jboss.resource.adapter.jms.JmsConnectionFactory</connection-definition>
      <config-property name="SessionDefaultType" type="java.lang.String">javax.jms.Topic</config-property>
      <config-property name="JmsProviderAdapterJNDI" type="java.lang.String">java:/DefaultJMSProvider</config-property>
      <max-pool-size>20</max-pool-size>
      <!-- <security-domain-and-application>JmsXARealm</security-domain-and-application> -->
      <depends>jboss.messaging:service=ServerPeer</depends>
   </tx-connection-factory>

我也有非常简单的MDB,它只是将收到的消息输出到控制台(不会发布代码,因为它很简单)。

问题是,当在JMS代码中抛出异常时,MDB仍然会收到消息,并且在数据库代码中成功更新了SomeEntity(而我希望它能够回滚)。

这是JMS log。我看到的一个可疑的事情就是:

received ONE_PHASE_COMMIT request

就像我说的那样,我对XA还不太熟悉,但我希望在这里看到TWO_PHASE_COMMIT,因为应该有2个参与活动交易的资源。

非常感谢任何帮助。

更新

在我尝试了@ djmorton的建议后,它终于奏效了。 使用JBoss 5.1时要记住的另一个重要事项是XA JMS ConnectionFactory的查找名称是&#34; java:/ JmsXA&#34;。我用

尝试了同样的方法
@Resource(mappedName = "XAConnectionFactory")
private ConnectionFactory connFactory;

它没有用。

1 个答案:

答案 0 :(得分:3)

在将sendMsg()方法抛入其中后,您将捕获RuntimeException。除非它被抛出堆栈,否则Exception不会触发事务回滚。使用Container托管事务时,容器会向方法调用添加拦截器,以便在抛出未经检查的异常时设置事务并处理回滚。如果异常没有被抛出该方法,拦截器就不知道它需要对该事务起作用。

编辑1:

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

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

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

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