Spring使用MyBatis 集成Atomikos 实现分布式事务 管理多数据源

项目结构: 

2.实体类(order类;logInfo类)

package com.lucifer.pojo;

import lombok.Data;

import java.util.Date;

/**
 * @author Lucifer
 */
@Data
public class LogInfo {

    private Integer id;

    private Date createTime;

    private String content;

}
package com.lucifer.pojo;

import lombok.Data;

import java.util.Date;

/**
 * @author Lucifer
 */
@Data
public class OrderInfo {

    private Integer id;

    private Double money;

    private String userId;

    private String address;

    private Date createTime;

}

3.mapper接口(order、log)

package com.lucifer.order.mapper;

import com.lucifer.pojo.OrderInfo;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author Lucifer
 */
public interface OrderMapper {

    /**
     * 新增
     * @param orderInfo
     * @return
     */
    int insertOrderInfo(OrderInfo orderInfo);

    /**
     * 批量新增
     * @param orderInfos
     * @return
     */
    int insertBatchOrder(@Param("orderInfos") List<OrderInfo> orderInfos);
}
package com.lucifer.log.mapper;

import com.lucifer.pojo.LogInfo;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author Lucifer
 */
public interface LogMapper {

    /**
     * 新增
     * @param logInfo
     * @return
     */
    int insertLogInfo(LogInfo logInfo);

    /**
     * 批量新增
     * @param logInfoList
     * @return
     */
    int insertBatchLogs(@Param("logInfoList") List<LogInfo> logInfoList);
}

4.service接口及实现类

package com.lucifer.service;

import com.lucifer.pojo.OrderInfo;

import java.util.List;

/**
 * @author Lucifer
 */
public interface OrderService {

    /**
     * 单个新增
     *
     * @param orderInfo
     */
    void addOrder(OrderInfo orderInfo);

    /**
     * 批量新增
     * @param orderInfos
     */
    void addBatchOrder(List<OrderInfo> orderInfos);

}

因为要测试单个新增和批量新增的,所以这里写了两个接口实现; 

package com.lucifer.service.impl;


import com.lucifer.log.mapper.LogMapper;
import com.lucifer.order.mapper.OrderMapper;
import com.lucifer.pojo.LogInfo;
import com.lucifer.service.OrderService;
import org.springframework.stereotype.Service;
import com.lucifer.pojo.OrderInfo;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;

/**
 * @author Lucifer
 */
@Service
public class OrderServiceImpl implements OrderService {

    @Resource
    OrderMapper orderMapper;

    @Resource
    LogMapper logMapper;

    public void addOrder(OrderInfo orderInfo) {

        //新增(order库)
        int i=orderMapper.insertOrderInfo(orderInfo);
        System.out.println("order库,执行sql条数:"+i);

        //测试1
        //int k=1/0;

        //新增 (logs库)
        LogInfo logInfo = new LogInfo();
        logInfo.setId(new Random().nextInt());
        logInfo.setCreateTime(new Date());
        logInfo.setContent(orderInfo.toString());
        int i1 = logMapper.insertLogInfo(logInfo);
        System.out.println("logs库,执行sql条数:"+i1);
        //测试2
        int k=1/0;
    }

    public void addBatchOrder(List<OrderInfo> orderInfos) {
        int i=orderMapper.insertBatchOrder(orderInfos);
        System.out.println("order库,执行sql条数:"+i);

        //测试1
       // int k=1/0;

        //新增 (logs库)
        List<LogInfo> logInfoList=new ArrayList<LogInfo>();
        for(int j=0;j<orderInfos.size();j++){
            LogInfo logInfo = new LogInfo();
            logInfo.setId(new Random().nextInt());
            logInfo.setCreateTime(new Date());
            logInfo.setContent(orderInfos.get(j).toString());
            logInfoList.add(logInfo);
        }
        int i1 =logMapper.insertBatchLogs(logInfoList);
        System.out.println("logs库,执行sql条数:"+i1);
        //测试2
        int k=1/0;
    }
}

5.mapper.xml

  (1)logMapper.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.lucifer.log.mapper.LogMapper">

    <insert id="insertLogInfo">
        insert into log_info(id,create_time,content) values(#{id},#{createTime},#{content})
    </insert>

    <insert id="insertBatchLogs">
        insert into log_info
            (id,create_time,content)
        values
        <foreach collection="logInfoList" item="log" index="index" separator=",">
            (#{log.id},#{log.createTime},#{log.content})
        </foreach>
    </insert>
</mapper>

(2)OrderMapper.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.lucifer.order.mapper.OrderMapper">

    <insert id="insertOrderInfo">
        insert into order_info(id,money,user_id,address,create_time)
        values(#{id},#{money},#{userId},#{address},#{createTime})
    </insert>


    <insert id="insertBatchOrder">
        insert into order_info
            (id,money,user_id,address,create_time)
        values
        <foreach collection="orderInfos" item="order" index="index" separator=",">
            (#{order.id},#{order.money},#{order.userId},#{order.address},#{order.createTime})
        </foreach>

    </insert>


</mapper>

6.jdbc.properties(多数据源)

#===================================数据源配置===============================
#order数据库
jdbc.order.driver=com.mysql.jdbc.Driver
jdbc.order.url=jdbc:mysql://192.168.87.130:3306/order?useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false
jdbc.order.username=root
jdbc.order.password=123456

#logs数据库
jdbc.log.driver=com.mysql.jdbc.Driver
jdbc.log.url=jdbc:mysql://192.168.87.130:3306/logs?useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false
jdbc.log.username=root
jdbc.log.password=123456

7.spring.xml(最重要的配置)

<?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:aop="http://www.springframework.org/schema/aop"
       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/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--加载配置文件-->
    <context:property-placeholder location="jdbc.properties" />

    <!--包扫描-->
    <context:component-scan base-package="com.lucifer" />

    <!--@Aspect-->
    <aop:aspectj-autoproxy/>

    <!--使用CGLIB动态代理-->
    <tx:annotation-driven transaction-manager="springTransactionManager" proxy-target-class="true" />
    <!--配置事务的通知-->
    <!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean
        below) 事务传播特性配置 -->
    <tx:advice id="txAdvice" transaction-manager="springTransactionManager">
        <!-- the transactional semantics... -->
        <tx:attributes>
            <tx:method name="add*" propagation="REQUIRED" isolation="DEFAULT"
                       rollback-for="java.lang.Exception" />
            <tx:method name="save*" propagation="REQUIRED" isolation="DEFAULT"
                       rollback-for="java.lang.Exception" />
            <tx:method name="insert*" propagation="REQUIRED" isolation="DEFAULT"
                       rollback-for="java.lang.Exception" />
            <tx:method name="update*" propagation="REQUIRED" isolation="DEFAULT"
                       rollback-for="java.lang.Exception" />
            <tx:method name="modify*" propagation="REQUIRED" isolation="DEFAULT"
                       rollback-for="java.lang.Exception" />
            <tx:method name="delete*" propagation="REQUIRED" isolation="DEFAULT"
                       rollback-for="java.lang.Exception" />
            <!-- 查询方法 -->
            <tx:method name="query*" read-only="true" />
            <tx:method name="select*" read-only="true" />
            <tx:method name="find*" read-only="true" />
        </tx:attributes>
    </tx:advice>


    <!--
        配置事务管理器atomikos事务管理器
     -->
    <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close">
        <property name="forceShutdown" value="false"/>
    </bean>
    <!--
        本地事务管理器
    -->
    <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
        <property name="transactionTimeout" value="300000"/>
    </bean>

    <!--JTA事务管理器-->
    <bean id="springTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="transactionManager">
            <ref bean="atomikosTransactionManager"/>
        </property>
        <property name="userTransaction">
            <ref bean="atomikosUserTransaction"/>
        </property>
        <property name="allowCustomIsolationLevels" value="true"/>
    </bean>


    <!--数据源基础配置-->
    <bean id="abstractXADataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close" abstract="true">
        <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"/>
        <property name="poolSize" value="10"/>
        <property name="minPoolSize" value="10"/>
        <property name="maxPoolSize" value="30"/>
        <property name="borrowConnectionTimeout" value="60"/>
        <property name="reapTimeout" value="20"/>
        <property name="maxIdleTime" value="60"/>
        <property name="maintenanceInterval" value="60"/>
        <property name="testQuery">
            <value>SELECT 1</value>
        </property>
    </bean>


    <!-- 数据库基本信息配置 -->
    <bean id="dataSourceOrder" parent="abstractXADataSource">
        <property name="uniqueResourceName">
            <value>dataSourceOrder</value>
        </property>
        <!--数据库驱动-->
        <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"/>
        <property name="xaProperties">
            <props>
                <prop key="URL">${jdbc.order.url}</prop>
                <prop key="user">${jdbc.order.username}</prop>
                <prop key="password">${jdbc.order.password}</prop>
            </props>
        </property>
    </bean>

    <!--日志数据源-->
    <bean id="dataSourceLog" parent="abstractXADataSource">
        <property name="uniqueResourceName">
            <value>dataSourceLog</value>
        </property>
        <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"/>
        <property name="xaProperties">
            <props>
                <prop key="URL">${jdbc.log.url}</prop>
                <prop key="user">${jdbc.log.username}</prop>
                <prop key="password">${jdbc.log.password}</prop>
            </props>
        </property>
    </bean>


    <!--SqlSessionFactoryBean的配置-->
    <bean id="sqlSessionFactoryBeanOrder" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="typeAliasesPackage" value="com.lucifer.pojo" />
        <property name="mapperLocations">
        <array>
            <value>classpath:/mapper/*Mapper.xml</value>
        </array>
        </property>
        <property name="dataSource" ref="dataSourceOrder"/>
    </bean>

    <!--操作日志数据源的SqlSessionFactoryBean-->
    <bean id="sqlSessionFactoryBeanLog" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="typeAliasesPackage" value="com.lucifer.pojo" />
        <property name="mapperLocations">
            <array>
                <value>classpath:/mapper/*Mapper.xml</value>
            </array>
        </property>
        <property name="dataSource" ref="dataSourceLog"/>
    </bean>


    <!--包扫描,订单Dao包扫描-->
    <bean id="mapperScannerConfigurerOrder" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.lucifer.order.mapper" />
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBeanOrder" />
    </bean>

    <!--包扫描,日志Dao包扫描-->
    <bean id="mapperScannerConfigurerLog" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.lucifer.log.mapper" />
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBeanLog" />
    </bean>

    <!-- 声明式事务AOP配置,配置aop切入点表达式 -->
    <aop:config>
        <aop:pointcut expression="execution(* com.lucifer.service.impl.*.*(..))" id="tranpointcut" />

        <!--声明式事务通知,配置切入点表倒是-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="tranpointcut" />
    </aop:config>



</beans>

8.测试方法

package com.lucifer.test;

import com.lucifer.pojo.OrderInfo;
import com.lucifer.service.OrderService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring.xml")
public class test {

    @Resource
    OrderService orderService;

    @Test
    public void test1() {
        //等同@ContextConfiguration("classpath:spring.xml")
        //ClassPathXmlApplicationContext cat = new ClassPathXmlApplicationContext("spring.xml");
        //等同 @Resource  OrderService orderService;
       // OrderService orderService = cat.getBean(OrderService.class);

        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setId((int) (Math.random()*1000));
        orderInfo.setAddress("上海");
        orderInfo.setCreateTime(new Date());
        orderInfo.setUserId("路西法");
        orderInfo.setMoney(666d);
        orderService.addOrder(orderInfo);
    }

    @Test
    public void test2() {
        List<OrderInfo> orderInfoList=new ArrayList<OrderInfo>();
        for(int i=0;i<10;i++){
            OrderInfo orderInfo = new OrderInfo();
            orderInfo.setId((new Random().nextInt()));
            orderInfo.setAddress("上海");
            orderInfo.setCreateTime(new Date());
            orderInfo.setUserId("路西法");
            orderInfo.setMoney(new Random().nextDouble());
            orderInfoList.add(orderInfo);
        }
        orderService.addBatchOrder(orderInfoList);
    }



}

效果图:

(1)测试方法test1()中的测试1:order库sql执行新增了一条,然后异常出现,logs库中sql插入未执行,开始回滚order库新增操作

说明order数据库执行操作回滚了;(效果是期待的)

同理,其它操作就不截图了。

附:sql:(分别放在两个库中)

CREATE TABLE `log_info` (
  `id` int(11) NOT NULL,
  `create_time` datetime DEFAULT NULL,
  `content` longtext,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


CREATE TABLE `order_info` (
  `id` int(11) NOT NULL,
  `money` double NOT NULL,
  `user_id` varchar(20) DEFAULT NULL,
  `address` varchar(200) DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

附:pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>transaction_example</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>spring-atomikos</artifactId>

    <!--常量和版本号-->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <aspectj.version>1.8.6</aspectj.version>
        <aspectj.weaver>1.8.6</aspectj.weaver>
        <mybatis.spring.version>1.3.0</mybatis.spring.version>
        <mybatis.version>3.4.5</mybatis.version>
        <mysql.version>5.1.48</mysql.version>
        <junit.version>4.12</junit.version>
        <spring.version>5.0.2.RELEASE</spring.version>
        <jta.version>1.1</jta.version>
        <atomikos.version>4.0.6</atomikos.version>
        <cglib.nodep.version>3.2.5</cglib.nodep.version>
        <druid.version>1.0.13</druid.version>
    </properties>

    <dependencies>
        <!--JTA atomikos-->
        <dependency>
            <groupId>javax.transaction</groupId>
            <artifactId>jta</artifactId>
            <version>${jta.version}</version>
        </dependency>
        <dependency>
            <groupId>com.atomikos</groupId>
            <artifactId>atomikos-util</artifactId>
            <version>${atomikos.version}</version>
        </dependency>
        <dependency>
            <groupId>com.atomikos</groupId>
            <artifactId>transactions</artifactId>
            <version>${atomikos.version}</version>
        </dependency>
        <dependency>
            <groupId>com.atomikos</groupId>
            <artifactId>transactions-jta</artifactId>
            <version>${atomikos.version}</version>
        </dependency>
        <dependency>
            <groupId>com.atomikos</groupId>
            <artifactId>transactions-jdbc</artifactId>
            <version>${atomikos.version}</version>
        </dependency>
        <dependency>
            <groupId>com.atomikos</groupId>
            <artifactId>transactions-api</artifactId>
            <version>${atomikos.version}</version>
        </dependency>

        <!--aop动态代理-->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib-nodep</artifactId>
            <version>${cglib.nodep.version}</version>
        </dependency>


        <!--数据连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>${druid.version}</version>
        </dependency>


        <!-- spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!-- spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!-- AspectJ Runtime -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>${aspectj.version}</version>
        </dependency>

        <!-- AspectJ Weaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>${aspectj.weaver}</version>
        </dependency>

        <!-- Spring Jdbc 的支持 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!-- mybatis-spring 整合 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>${mybatis.spring.version}</version>
        </dependency>

        <!-- mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>${mybatis.version}</version>
        </dependency>

        <!-- MySql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!-- Test dependencies -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>

        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
        </dependency>

    </dependencies>



</project>
发布了187 篇原创文章 · 获赞 146 · 访问量 49万+

猜你喜欢

转载自blog.csdn.net/qq_37495786/article/details/103197668