`

分布式java事务一(非原创)

阅读更多

Java事务处理全解析(八)——分布式事务入门例子(Spring+JTA+Atomikos+Hibernate+JMS) 
在本系列先前的文章中,我们主要讲解了JDBC对本地事务的处理,本篇文章将讲到一个分布式事务的例子。 

  

请通过以下方式下载github源代码: 

git clone https://github.com/davenkin/jta-atomikos-hibernate-activemq.git 

本地事务和分布式事务的区别在于:本地事务只用于处理单一数据源事务(比如单个数据库),分布式事务可以处理多种异构的数据源,比如某个业务操作中同时包含了JDBC和JMS或者某个操作需要访问多个不同的数据库。 

  

Java通过JTA完成分布式事务,JTA本身只是一种规范,不同的应用服务器都包含有自己的实现(比如JbossJTA),同时还存在独立于应用服务器的单独JTA实现,比如本篇中要讲到的Atomikos。对于JTA的原理,这里不细讲,读者可以通过这篇文章了解相关知识。 

  

在本篇文章中,我们将实现以下一个应用场景:你在网上购物,下了订单之后,订单数据将保存在系统的数据库中,同时为了安排物流,订单信息将以消息(Message)的方式发送到物流部门以便送货。 

  

以上操作同时设计到数据库操作和JMS消息发送,为了使整个操作成为一个原子操作,我们只能选择分布式事务。我们首先设计一个service层,定义OrderService接口: 

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对象进行Marshal和Unmarshal,我们在Order类中加入了JAXB相关的Annotation。 我们将使用Hibernate来完成数据持久化,然后使用Spring提供的JmsTemplate将Order转成xml后以TextMessage的形式发送到物流部门的ORDER.QUEUE中。 

  

(一)准备数据库 

为了方便,我们将采用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,在测试执行完之后关闭BrokerService。 


  @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(); 
    } 

  

(三)实现OrderService 

创建一个DefaultOrderService,该类实现了OrderService接口,并维护一个JmsTemplate和一个Hibernate的SessionFactory实例变量,分别用于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提供的UserTransaction和TransactionManager,然后再配置Spring的JtaTransactionManager: 


  <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加入到分布式事务中,我们需要配置ActiveMQXAConnectionFactory,而不是ActiveMQConnectionFactory,然后再配置JmsTemplate,此外还需要配置MessageConvertor在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,再调用DefaultOrderService,最后对数据库和QUEUE进行验证: 


   @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()); 
    } 

分享到:
评论

相关推荐

    Java原创酒店订房系统源码+论文(已通过答辩)

    【Java原创酒店订房系统源码+论文(已通过答辩)】是一个综合性的项目,它展示了如何使用Java编程语言来构建一个完整的酒店订房系统。这个系统不仅包含了前端用户界面,还涵盖了后端数据处理、业务逻辑以及可能的...

    java2018面试宝典

    本文是一份关于Java面试的全面复习资料,涵盖了2018年Java面试中常见的知识点。接下来,我将详细解析文档中提到的各个...这本复习资料强调原创性,鼓励读者发现错误时可以贡献修改,同时也开放了非商业性的使用授权。

    w3school java & java web教程 飞龙整理 20141001

    2. **Java Enterprise Edition (JavaEE)**:面向企业级应用,如Web应用、数据库连接、事务处理等。它包含JavaSE的所有功能,并扩展了诸如Servlet、JSP(Java Server Pages)、EJB(Enterprise JavaBeans)等技术。 ...

    java开发毕业设计(亲测黑科技大学)

    Java是一种广泛使用的面向对象的编程语言,以其跨平台、安全性和可移植性而闻名,特别适合于构建大型、分布式系统以及网络应用程序。 【描述】提到的“早期毕业的时候在淘宝买的”,可能是指购买了相关的参考资料或...

    美河原创作品]J2EE系列讲座

    4. **JTA(Java Transaction API)**:用于管理事务,确保在分布式环境中数据的一致性和完整性。 5. **JMS(Java Message Service)**:支持异步通信,允许应用程序通过消息传递进行解耦。 6. **JPA(Java ...

    原创毕业设计翻译—J2EE—外国论文原文+翻译

    **J2EE**,是Java平台的一个版本,专为构建和部署分布式企业级应用程序而设计。它提供了一个开放的标准框架,支持多种服务如Web服务器、应用服务器、数据库连接、事务处理、安全性和多层架构。J2EE的核心组件包括...

    [美河原创作品]J2EE系列讲座

    总之,【美河原创作品】J2EE系列讲座是Java开发者提升J2EE技能的理想资源,特别是对于想要掌握Spring AOP的初学者来说,"eimheSpringAopDemo1"提供了一个宝贵的实践平台,能够帮助他们将理论知识转化为实际操作,...

    Ehcache经典教程实例应用-原创

    Ehcache是一款广受欢迎的Java缓存框架,它的主要目的是缓解数据库压力,提升应用程序的访问速度。通过在内存中存储常用数据,Ehcache可以减少对数据库的频繁访问,从而提高整体系统性能。本文将深入探讨Ehcache的...

    2021面试指南(含各大简历模板)资料2.zip

    Tom_原创_27道大厂高频Spring面试,95%的人都答不上.doc和Spring面试题与答案.doc可能包含对Spring核心概念、Bean生命周期、Spring MVC、AOP、事务管理等方面的深度问题和解答。 2. **Spring源码分析**:Tom_深入...

    基于J2EE网上购书系统的设计与实现毕业设计论文.doc

    J2EE(Java 2 Platform, Enterprise Edition)是一个广泛应用于企业级应用开发的标准平台,它包含了多种技术规范,如Java Server Pages (JSP)、Java Servlet、Enterprise JavaBeans (EJB)、Java Message Service ...

    TutorialsPoint SpringJDBC JSoup CouchDB Cassandra JSF Kotlin 教程

    SpringJDBC提供了一层抽象,可以处理数据库连接、事务管理和异常处理,让开发者更专注于SQL查询和数据操作。通过使用模板方法和DAO支持,它使得在Java应用中集成数据库变得更加容易和直观。 2. JSoup:一个强大的...

    web上的图书管理网站 的代码

    它包含了Java Servlet、JavaServer Pages(JSP)和JavaBean等组件,以及用于处理事务、安全、分布式计算等高级服务。 “图书管理”标签表明了这个项目的核心功能,即管理图书馆中的书籍信息,包括书籍的录入、查询...

    帮做基于J2EE框架的购物商城管理系统.docx

    J2EE,全称为Java 2 Platform, Enterprise Edition,是Oracle公司推出的用于构建分布式、多层架构的企业级应用程序的平台。这个平台包含了多种服务、API和容器,能够支持Web应用程序、EJB(Enterprise JavaBeans)、...

    JavaWeb上机练习8.zip

    EJB提供了事务管理、安全性和资源池等功能,适用于大型分布式系统。 8. **JDBC(Java Database Connectivity)**: JDBC是Java访问数据库的标准API,通过JDBC,开发者可以连接到各种类型的数据库,执行SQL语句,处理...

    让Spring之父Rod Johnson拍案叫绝的原创经典(spring解密)

    3. **AOP(面向切面编程)**:Spring的另一个重要特性是AOP,它允许我们定义横切关注点,如日志、事务管理等,并将它们模块化,独立于业务逻辑。这样,我们可以编写更简洁、更专注的代码。 4. **Bean管理**:Spring...

    传智播客 Spring、SpringMVC、Hibernate、MyBatis、Struts、Maven、SVN、大数据讲义

    1. **Spring框架**:Spring是Java企业级应用中的核心框架,它提供了一个全面的编程和配置模型,使得开发者可以更高效地构建可测试、松耦合的应用。Spring的核心特性包括依赖注入、面向切面编程(AOP)、事务管理等。...

    基于J2EE的问卷调查系统的毕业设计论文.pdf

    J2EE是一种广泛应用于企业级应用开发的平台,它提供了多层架构,支持分布式计算和web应用程序的开发。 在设计这样的系统时,首先需要理解问卷调查的基本流程,包括创建问卷、发布问卷、用户填写问卷、收集数据以及...

    Redis学习笔记大全

    * SMOVE:将某个原创从一个集合移动到另一个集合 * SINTER:取多个集合的交集 * SINTERSTORE:将多个集合的交集放到一个新的集合中 * SUNION:取多个集合的并集,自动去重 * SUNIONSTORE:将多个集合的并集放到一个...

Global site tag (gtag.js) - Google Analytics