在 JBoss 中设置分布式事务管理的步骤

Steps to setup distributed transaction management in JBoss

我正在尝试在 JBoss EAP 6.2 应用程序服务器中实现分布式事务 (XA),以便可以在单个协调的原子事务中访问多个数据存储。更准确地说,我希望我的事务服务方法以这样的方式写入数据库 table 和消息队列,使得这两个操作要么同时提交,要么一致回滚(全部或全部) .

我的方法基于以下几点:

我遇到的问题是只有数据库操作被回滚了。无论事务回滚与否,写入ActiveMQ队列的消息总是提交。

我的配置的关键要素:

<tx:jta-transaction-manager/>

<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="jtaDataSource" ref="xaDataSource" />
    ...
    <property name="jpaProperties">
        <props>
            ...
            <prop key="hibernate.transaction.manager_lookup_class">org.hibernate.transaction.JBossTransactionManagerLookup</prop>
        </props>
    </property>
</bean>

<jee:jndi-lookup id="xaDataSource" jndi-name="xaDataSource"/>

<bean id="xaConnectionFactory" class="org.apache.activemq.ActiveMQXAConnectionFactory">
    <property name="brokerURL"> 
        <value>tcp://localhost:61616</value> 
    </property> 
</bean>

<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
    <property name="connectionFactory" ref="xaConnectionFactory" />
    <property name="defaultDestinationName" value="TEST_QUEUE" />
    <property name="sessionTransacted" value="true"/>
</bean>

我终于搞定了。关键是在 JBoss 资源适配器中配置 JMS 连接工厂。详细步骤如下:

1。安装 Apache ActiveMQ

使用的版本:5.11.3

可以找到详细的安装说明 here

安装 ActiveMQ 后,创建一个名为 TEST_QUEUE 的队列(使用管理控制台:http://127.0.0.1:8161/admin/index.jsp

2。设置 Spring 应用程序上下文

关键要素:

  • 使用Spring JTA事务管理器标签:这会提示使用 应用服务器事务管理器;
  • 配置数据源 bean 以使用应用服务器中定义的 XA 数据源(参见 XA JDBC 数据源设置);
  • 将实体管理器工厂的 jtaDataSource 属性连接到 XA 数据源;
  • 将休眠 属性 manager_lookup_class 设置为 JBossTransactionManagerLookup;
  • 配置连接工厂 bean 以使用应用程序服务器中定义的 XA 连接工厂 bean(参见 XA 连接工厂设置);
  • 将连接工厂 bean 的 属性 transactedSession 设置为 false

e

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xmlns:jms="http://www.springframework.org/schema/jms"   xmlns:jee="http://www.springframework.org/schema/jee"
    xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
        http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd">

<jpa:repositories base-package="com.company.app.repository" />

<context:component-scan base-package="com.company.app" />

<tx:annotation-driven transaction-manager="transactionManager"/>

<tx:jta-transaction-manager/>

<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="jtaDataSource" ref="xaDataSource" />
    <property name="packagesToScan" value="com.company.app.domain" />
    <property name="persistenceProviderClass" value="org.hibernate.ejb.HibernatePersistence" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="databasePlatform" value="org.hibernate.dialect.Oracle10gDialect" />
        </bean>
    </property>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.transaction.manager_lookup_class">org.hibernate.transaction.JBossTransactionManagerLookup</prop>
        </props>
    </property>
</bean>

<jee:jndi-lookup id="xaDataSource" jndi-name="jdbc/xaDataSource"/>

<bean id="xaConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="activemq/ConnectionFactory" />
</bean>

<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
    <property name="connectionFactory" ref="xaConnectionFactory" />
    <property name="defaultDestinationName" value="TEST_QUEUE" />
    <property name="sessionTransacted" value="false"/>
</bean>

3。设置应用程序服务器(JBoss)

要使分布式事务正常工作,所有涉及的数据源都必须是 XA 类型。 JBoss 开箱即用地支持 JDBC XA 数据源(xa-datasource 标签)。 JMS 数据源的 XA 配置是通过定义适当的资源适配器来实现的。

3.1。 XA JDBC 数据源

<subsystem xmlns="urn:jboss:domain:datasources:1.1"> <datasources> 下的 standalone.xml 添加一个 XA JDBC 数据源:

<xa-datasource jndi-name="java:/jdbc/xaDataSource" pool-name="jdbc/xaDataSource" enabled="true">
    <xa-datasource-property name="URL">
        jdbc:oracle:thin:@<hostname>:<port_number>/<SID>
    </xa-datasource-property>
    <xa-datasource-class>oracle.jdbc.xa.client.OracleXADataSource</xa-datasource-class>
    <driver>ojdbc6-11.2.0.3.jar</driver>
    <security>
        <user-name>db_user</user-name>
        <password>password</password>
    </security>
</xa-datasource>

3.2。 XA 连接工厂

资源适配器是来自 J2EE 连接器架构 (JCA) 的概念,用于与企业信息系统交互,即应用服务器外部的系统(例如,关系数据库、大型机、面向消息的中间件、会计系统)等)。

首先,您需要在 JBoss 中安装 ActiveMQ RAR(资源适配器 ARchive),方法是从 maven central 下的 \standalone\deployments 中删除适当的 RAR 文件。然后,在 standalone.xml 中的 <subsystem xmlns="urn:jboss:domain:resource-adapters:1.1"> 下添加以下内容:

<resource-adapters>
    <resource-adapter id="activemq-rar.rar">
        <archive>
            activemq-rar-5.11.3.rar
        </archive>
        <transaction-support>XATransaction</transaction-support>
        <config-property name="Password">
            admin
        </config-property>
        <config-property name="UserName">
            admin
        </config-property>
        <config-property name="ServerUrl">
            tcp://localhost:61616?jms.rmIdFromConnectionId=true
        </config-property>
        <connection-definitions>
            <connection-definition class-name="org.apache.activemq.ra.ActiveMQManagedConnectionFactory" jndi-name="java:/activemq/ConnectionFactory" enabled="true" pool-name="ConnectionFactory">
                <xa-pool>
                    <min-pool-size>1</min-pool-size>
                    <max-pool-size>20</max-pool-size>
                    <prefill>false</prefill>
                    <is-same-rm-override>false</is-same-rm-override>
                </xa-pool>
            </connection-definition>
        </connection-definitions>
    </resource-adapter>
</resource-adapters>

有关在 JBoss 中安装 ActiveMQ RAR 的更多详细信息,请参阅 RedHat documentation

4。使您的服务方法具有事务性

@Service
public class TwoPhaseCommitService {

    @Autowired
    private EmployeeRepository employeeRepository;

    @Autowired
    private JmsTemplate jmsTemplate;

    @Transactional
    public void writeToDbAndQueue() {

        final Employee employee = new Employee();

        employee.setFirstName("John");
        employee.setLastName("Smith");

        // persist entity to database
        employeeRepository.save(employee);

        // write message to TEST_QUEUE
        jmsTemplate.send(new MessageCreator() {
            public Message createMessage(Session session) throws JMSException {
                return session.createTextMessage(employee.getFirstName());
            }
        });

        // To test rollback uncomment code below:
        // throw new RuntimeException("something went wrong. Transaction must be rolled back!!!");
    }
}