分散式事務Spring+JTA+Atomikos+Hbernate+JMS

8699 ワード

以下の方法でgithubソースコードをダウンロードしてください。
git clone https://github.com/davenkin/jta-atomikos-hibernate-activemq.git
ローカル事務と分散事務の違いは、ローカル事務は単一のデータソーストランザクション(例えば、単一のデータベース)を処理するためにのみ使用され、分散事務は複数の異性のデータソースを処理することができます。例えば、ある業務操作には、JDBCとJMSまたはある操作が複数の異なるデータベースにアクセスする必要があります。
JavaはJTAを通じて分散事務を完成しました。JTA自体は標準的なもので、アプリケーションサーバによって自分の実現(例えばJbossJTA)が含まれています。JTAの原理については、ここで詳しく説明しないと、読者はこの文章を通じて関連知識を知ることができます。
本記事では、オンラインショッピングで注文した後、注文データをシステムのデータベースに保存し、物流を手配するために、注文情報をメッセージで送ります。
以上の操作は同時にデータベース操作とJMSメッセージ送信に設計されています。全体の動作を原子操作にするためには、分散式事務を選択するしかないです。まずservice層を設計し、Order Serviceインターフェースを定義します。
package davenkin;

public interface OrderService {

    public void makeOrder(Order order);
}
簡単のために、非常に簡単な領域オブジェクトOrderを設計します。
@XmlRootElement(name = "Order")
@XmlAccessorType(XmlAccessType.FIELD)
public class Order {

    @XmlElement(name = "Id",required = true)
    private long id;

    @XmlElement(name = "ItemName",required = true)
    private String itemName;

    @XmlElement(name = "Price",required = true)
    private double price;

    @XmlElement(name = "BuyerName",required = true)
    private String buyerName;

    @XmlElement(name = "MailAddress",required = true)
    private String mailAddress;

    public Order() {
    }
JAXBを採用して、Orderの対象に対してMasharlとUnimasharlを行うために、私達はOrder類にJAXB関連のAnnotationを入れました。私たちはヒップホップを使用してデータの持続化を完成し、Springで提供されたJmsTemplateを使用してOrderをxmlに変換して、TextMessageとして物流部門のORDER.QUUEに送信します。
(一)データベースを準備し、便利のためにSpringから提供されるembeddedデータベースを採用します。デフォルトではSpringはHSQLをバックグラウンドデータベースとして採用しています。この例ではHSQLの非XAのDataSourceを採用しますが、Atomikos包装を通じて、布地事務に参加することができます。SQLスクリプトはcreateDB.sqlファイルに含まれています。
CREATE TABLE USER_ORDER(
ID INT NOT NULL,
ITEM_NAME VARCHAR (100) NOT NULL UNIQUE,
PRICE DOUBLE NOT NULL,
BUYER_NAME CHAR (32) NOT NULL,
MAIL_ADDRESS VARCHAR(500) NOT NULL,
PRIMARY KEY(ID)
);
SpringにDataSourceを配置すると以下のようになります。
<jdbc:embedded-database id="dataSource">
    <jdbc:script location="classpath:createDB.sql"/>
</jdbc:embedded-database>
(二)ActiveMQの起動
embeddedのActiveMQを採用して、テストの前にActiveMQから提供されたBrokerServiceを起動して、テストの実行が終わったらBroker Serviceを閉じます。
@BeforeClass
    public static void startEmbeddedActiveMq() throws Exception {
        broker = new BrokerService();
        broker.addConnector("tcp://localhost:61616");
        broker.start();
    }

    @AfterClass
    public static void stopEmbeddedActiveMq() throws Exception {
        broker.stop();
    }
(三)Order Serviceの実現
一つのDefaultOrderServiceを作成して、このクラスはOrder Serviceインターフェースを実現して、一つのJmsTemplateと一つのHVarnateのSession Factoryインスタンス変数を維持して、それぞれMessageの送信とデータベース処理に使用します。
package davenkin;

import org.hibernate.SessionFactory;
import org.hibernate.classic.Session;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.transaction.annotation.Transactional;

public class DefaultOrderService  implements OrderService{
    private JmsTemplate jmsTemplate;
    private SessionFactory sessionFactory;

    @Override
    @Transactional
    public void makeOrder(Order order) {
        Session session = sessionFactory.getCurrentSession();
        session.save(order);
        jmsTemplate.convertAndSend(order);

    }

    @Required
    public void setJmsTemplate(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }

    @Required
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }
}
(四)OrderのMappingプロファイルを作成する
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
    <class name="davenkin.Order" table="USER_ORDER">
        <id name="id" type="long">
            <column name="ID" />
            <generator class="increment" />
        </id>
        <property name="itemName" type="string">
            <column name="ITEM_NAME" />
        </property>
        <property name="price" type="double">
            <column name="PRICE"/>
        </property>
        <property name="buyerName" type="string">
            <column name="BUYER_NAME"/>
        </property>
        <property name="mailAddress" type="string">
            <column name="MAIL_ADDRESS"/>
        </property>
    </class>
</hibernate-mapping>
(五)Atomikos事務の配置
SpringのIoC容器には、Atomikosから提供されるUserTransationとTransation Managerを配置してからSpringのJtaTransactionagerを配置する必要があります。
<bean id="userTransactionService" class="com.atomikos.icatch.config.UserTransactionServiceImp" init-method="init" destroy-method="shutdownForce">
        <constructor-arg>
            <props>
                <prop key="com.atomikos.icatch.service">com.atomikos.icatch.standalone.UserTransactionServiceFactory</prop>
            </props>
        </constructor-arg>
    </bean>

    <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close" depends-on="userTransactionService">
        <property name="forceShutdown" value="false" />
    </bean>

    <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp" depends-on="userTransactionService">
        <property name="transactionTimeout" value="300" />
    </bean>

    <bean id="jtaTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" depends-on="userTransactionService">
        <property name="transactionManager" ref="atomikosTransactionManager" />
        <property name="userTransaction" ref="atomikosUserTransaction" />
    </bean>

    <tx:annotation-driven transaction-manager="jtaTransactionManager" />
(六)JMSの配置
JMSに対しては、ActiveMQを分散式事務に参加させるために、ActiveMQAConnectoryを配置する必要があります。Active MQConnection Factoryではなく、JmsTemplateを再配置する必要があります。また、Message CovertorをOrderオブジェクトとXMLの間に配置する必要があります。
<bean id="jmsXaConnectionFactory" class="org.apache.activemq.ActiveMQXAConnectionFactory">
        <property name="brokerURL" value="tcp://localhost:61616" />
    </bean>

    <bean id="amqConnectionFactory" class="com.atomikos.jms.AtomikosConnectionFactoryBean" init-method="init">
        <property name="uniqueResourceName" value="XAactiveMQ" />
        <property name="xaConnectionFactory" ref="jmsXaConnectionFactory" />
        <property name="poolSize" value="5"/>
    </bean>


    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory" ref="amqConnectionFactory"/>
        <property name="receiveTimeout" value="2000" />
        <property name="defaultDestination" ref="orderQueue"/>
        <property name="sessionTransacted" value="true" />
        <property name="messageConverter" ref="oxmMessageConverter"/>
    </bean>

    <bean id="orderQueue" class="org.apache.activemq.command.ActiveMQQueue">
        <constructor-arg value="ORDER.QUEUE"/>
    </bean>


    <bean id="oxmMessageConverter"
          class="org.springframework.jms.support.converter.MarshallingMessageConverter">
        <property name="marshaller" ref="marshaller"/>
        <property name="unmarshaller" ref="marshaller"/>
    </bean>

    <oxm:jaxb2-marshaller id="marshaller">
        <oxm:class-to-be-bound name="davenkin.Order"/>
    </oxm:jaxb2-marshaller>
(七)テスト
テストでは、まず(二)の方法でActiveMQを起動し、DefaultOrder Serviceを呼び出して、最後にデータベースとQUUEを検証します。