项目结构:
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>