JMS不';不回滚XA事务(或不参与)
我对XA事务比较陌生。几天来,我一直在努力使一个简单的XA事务能够正常工作,但毫无结果 首先,我尝试使用两个不同的数据库。我设置了2个XA数据源,并在第二个数据库操作失败时成功回滚了第一个数据库操作。到目前为止,一切顺利。但后来我尝试用JMS connectionFactory替换第二个数据源,无法重现相同的行为 以下是相关代码: 数据库逻辑: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
@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 JMSConnectionFactory
的查找名称是“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事务。如果消息未被发送,则表示它已成功参与事务。