Java事務処理の全解析(8)——分散式事務(Spring+JTA+Atomikos+Hbernate+JMS)

9339 ワード

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

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getItemName() {
        return itemName;
    }

    public void setItemName(String itemName) {
        this.itemName = itemName;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public String getBuyerName() {
        return buyerName;
    }

    public void setBuyerName(String buyerName) {
        this.buyerName = buyerName;
    }

    public String getMailAddress() {
        return mailAddress;
    }

    public void setMailAddress(String mailAddress) {
        this.mailAddress = mailAddress;
    }
}
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を配置すると以下のようになります。
   
( )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の とデータベース に します。
public class DefaultOrderService  implements OrderService{
    private JmsTemplate jmsTemplate;
    private SessionFactory sessionFactory;

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

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

    @Required
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }
}
( )OrderのMappingプロファイルを する




    
        
            
            
        
        
            
        
        
            
        
        
            
        
        
            
        
    

( )Atomikos の
SpringのIoC には、Atomikosから されるUserTransationとTransation Managerを してからSpringのJtaTransactionagerを する があります。

       
           
               com.atomikos.icatch.standalone.UserTransactionServiceFactory
           
       
   

   
       
   

   
       
   

   
       
       
   

   

( )JMSの
JMSに しては、ActiveMQを に させるために、ActiveMQAConnectoryを する があります。Active MQConnection Factoryではなく、JmsTemplateを する があります。また、Message CovertorをOrderオブジェクトとXMLの に する があります。
   
       
   

   
       
       
       
   


   
       
       
       
       
       
   

   
       
   


   
       
       
   

   
       
   
( )テスト
テストでは、まず( )の でActiveMQを し、DefaultOrder Serviceを び して、 にデータベースとQUUEを します。
   @Test
   public void makeOrder() {
       orderService.makeOrder(createOrder());
       JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
       assertEquals(1, jdbcTemplate.queryForInt("SELECT COUNT(*) FROM USER_ORDER"));
       String dbItemName = jdbcTemplate.queryForObject("SELECT ITEM_NAME FROM USER_ORDER", String.class);
       String messageItemName = ((Order) jmsTemplate.receiveAndConvert()).getItemName();
       assertEquals(dbItemName, messageItemName);
   }

   @Test(expected = IllegalArgumentException.class)
   public void failToMakeOrder() {
       orderService.makeOrder(null);
       JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
       assertEquals(0, jdbcTemplate.queryForInt("SELECT COUNT(*) FROM USER_ORDER"));
       assertNull(jmsTemplate.receiveAndConvert());
   }

   private Order createOrder() {
       Order order = new Order();
       order.setBuyerName("davenkin");
       order.setItemName(randomName());
       order.setMailAddress("chengdu");
       order.setPrice(randomPrice());
       return order;
   }

   private String randomName() {
       return "book" + System.currentTimeMillis();
   }

   private double randomPrice() {
       String randomTimeString = String.valueOf(System.currentTimeMillis());
       return Double.parseDouble(randomTimeString.substring(randomTimeString.length() - 3));
   }