springboot2.0 解决多数据源分布式事务问题
大家都知道,在多数据源的项目中可能会发生的事务问题,因为一个库的事务不可能去操作另一个数据库的事务嘛,这里使用传统的分布式事务解决方案:SpringBoot+JTA+Atomikos
使用atomikos对数据库的事务进行统一的管理,但是这套解决方案不适合微服务,需要拿到数据源然后注册到同一个全局事务里面去
好,那我们如何配置呢?话不多说直接上代码
pom.xml配置
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4 <modelVersion>4.0.0</modelVersion> 5 <parent> 6 <groupId>org.springframework.boot</groupId> 7 <artifactId>spring-boot-starter-parent</artifactId> 8 <version>2.2.10.RELEASE</version> 9 <relativePath/> 10 </parent> 11 <groupId>com.wuwu</groupId> 12 <artifactId>cboot</artifactId> 13 <version>0.0.1-SNAPSHOT</version> 14 <name>cboot</name> 15 <description>this project for Spring Boot</description> 16 17 <properties> 18 <java.version>1.8</java.version> 19 </properties> 20 21 <dependencies> 22 <dependency> 23 <groupId>org.springframework.boot</groupId> 24 <artifactId>spring-boot-starter-data-jdbc</artifactId> 25 </dependency> 26 <dependency> 27 <groupId>org.springframework.boot</groupId> 28 <artifactId>spring-boot-starter-web</artifactId> 29 </dependency> 30 31 <!--jta-atomikos 解决多数据源分布式事物问题--> 32 <dependency> 33 <groupId>org.springframework.boot</groupId> 34 <artifactId>spring-boot-starter-jta-atomikos</artifactId> 35 </dependency> 36 <!-- mybatis --> 37 <dependency> 38 <groupId>org.mybatis.spring.boot</groupId> 39 <artifactId>mybatis-spring-boot-starter</artifactId> 40 <version>2.0.0</version> 41 </dependency> 42 <!--lombok--> 43 <dependency> 44 <groupId>org.projectlombok</groupId> 45 <artifactId>lombok</artifactId> 46 <version>1.18.12</version> 47 <scope>provided</scope> 48 </dependency> 49 50 <dependency> 51 <groupId>mysql</groupId> 52 <artifactId>mysql-connector-java</artifactId> 53 <scope>runtime</scope> 54 </dependency> 55 56 <dependency> 57 <groupId>ch.qos.logback</groupId> 58 <artifactId>logback-core</artifactId> 59 <version>1.1.3</version> 60 </dependency> 61 <dependency> 62 <groupId>ch.qos.logback</groupId> 63 <artifactId>logback-access</artifactId> 64 <version>1.1.3</version> 65 </dependency> 66 <dependency> 67 <groupId>ch.qos.logback</groupId> 68 <artifactId>logback-classic</artifactId> 69 <version>1.1.3</version> 70 </dependency> 71 72 <dependency> 73 <groupId>org.springframework.boot</groupId> 74 <artifactId>spring-boot-starter-test</artifactId> 75 <scope>test</scope> 76 <exclusions> 77 <exclusion> 78 <groupId>org.junit.vintage</groupId> 79 <artifactId>junit-vintage-engine</artifactId> 80 </exclusion> 81 </exclusions> 82 </dependency> 83 </dependencies> 84 85 <build> 86 <plugins> 87 <plugin> 88 <groupId>org.springframework.boot</groupId> 89 <artifactId>spring-boot-maven-plugin</artifactId> 90 </plugin> 91 </plugins> 92 </build> 93 94 </project>
application.properties配置
1 server.port=8090 2 server.servlet.context-path=/ 3 #单数据源 4 #spring.datasource.driverClassName=com.mysql.jdbc.Driver 5 #spring.datasource.url=jdbc:mysql://localhost:3306/cboot?useUnicode=true&amp;characterEncoding=UTF-8 6 #spring.datasource.username=root 7 #spring.datasource.password=root 8 9 #多数据源-订单 10 spring.datasource.order.driverClassName=com.mysql.jdbc.Driver 11 spring.datasource.order.url=jdbc:mysql://localhost:3306/cboot?useUnicode=true&amp;characterEncoding=UTF-8 12 spring.datasource.order.username=root 13 spring.datasource.order.password=root 14 15 #多数据源-会员 16 spring.datasource.member.driverClassName=com.mysql.jdbc.Driver 17 spring.datasource.member.url=jdbc:mysql://localhost:3306/cboot1?useUnicode=true&amp;characterEncoding=UTF-8 18 spring.datasource.member.username=root 19 spring.datasource.member.password=root
MemberConfig以及OrderConfig的配置bean
1 package com.wuwu.cboot.config; 2 3 import lombok.Data; 4 import org.springframework.boot.context.properties.ConfigurationProperties; 5 6 /** 7 * @Description:会员数据库连接属性 8 * @author: wph 9 * @Version: 1.0 10 * @date: 2021/1/15 11 */ 12 @Data 13 @ConfigurationProperties(prefix = "spring.datasource.member") 14 public class MemberConfig { 15 16 private String url; 17 private String password; 18 private String username; 19 private String driverClassName; 20 }
1 package com.wuwu.cboot.config; 2 3 import lombok.Data; 4 import org.springframework.boot.context.properties.ConfigurationProperties; 5 6 /** 7 * @Description:订单数据库连接属性 8 * @author: wph 9 * @Version: 1.0 10 * @date: 2021/1/15 11 */ 12 @Data 13 @ConfigurationProperties(prefix = "spring.datasource.order") 14 public class OrderConfig { 15 16 private String url; 17 private String password; 18 private String username; 19 private String driverClassName; 20 }
会员数据源MemberDataSourceConfig配置
1 package com.wuwu.cboot.config; 2 3 import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource; 4 import lombok.SneakyThrows; 5 import org.apache.ibatis.session.SqlSessionFactory; 6 import org.mybatis.spring.SqlSessionFactoryBean; 7 import org.mybatis.spring.SqlSessionTemplate; 8 import org.mybatis.spring.annotation.MapperScan; 9 import org.springframework.beans.factory.annotation.Qualifier; 10 import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean; 11 import org.springframework.context.annotation.Bean; 12 import org.springframework.context.annotation.Configuration; 13 14 import javax.sql.DataSource; 15 16 /** 17 * @Description:会员数据源配置 18 * @author: wph 19 * @Version: 1.0 20 * @date: 2021/1/12 21 */ 22 @Configuration 23 @MapperScan(basePackages = "com.wuwu.cboot.member.mapper",sqlSessionTemplateRef = "memberSqlSessionTemplate") 24 public class MemberDataSourceConfig { 25 26 /** 27 * @Description: 配置会员数据源 28 * @author: wph 29 * @date: 2021/1/15 30 * @param memberConfig 31 * @return javax.sql.DataSource 32 */ 33 @Bean("memberDataSource") 34 public DataSource memberDataSource(MemberConfig memberConfig){ 35 //1.创建会员的xaDataSource 36 MysqlXADataSource xaDataSource = new MysqlXADataSource(); 37 xaDataSource.setPassword(memberConfig.getPassword()); 38 xaDataSource.setUser(memberConfig.getUsername()); 39 xaDataSource.setUrl(memberConfig.getUrl()); 40 xaDataSource.setPinGlobalTxToPhysicalConnection(true); 41 42 //2.将会员的xaDataSource注册到全局事务上 43 AtomikosDataSourceBean dataSource = new AtomikosDataSourceBean(); 44 dataSource.setXaDataSource(xaDataSource); 45 46 return dataSource; 47 } 48 49 /** 50 * @Description: 将会员sqlSessionFactory注入到容器中 51 * @author: wph 52 * @date: 2021/1/13 53 * @param dataSource 54 * @return org.apache.ibatis.session.SqlSessionFactory 55 */ 56 @SneakyThrows(Exception.class) 57 @Bean("memberSqlSessionFactory") 58 public SqlSessionFactory memberSqlSessionFactory(@Qualifier("memberDataSource")DataSource dataSource){ 59 SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); 60 sqlSessionFactoryBean.setDataSource(dataSource); 61 return sqlSessionFactoryBean.getObject(); 62 } 63 64 /** 65 * @Description: 创建SqlSessionTemplate模板 66 * @author: wph 67 * @date: 2021/1/18 68 * @param sqlSessionFactory 69 * @return org.mybatis.spring.SqlSessionTemplate 70 */ 71 @Bean("memberSqlSessionTemplate") 72 public SqlSessionTemplate memberSqlSessionTemplate(@Qualifier("memberSqlSessionFactory") SqlSessionFactory sqlSessionFactory){ 73 return new SqlSessionTemplate(sqlSessionFactory); 74 } 75 76 }
订单数据源OrderDataSourceConfig配置
1 package com.wuwu.cboot.config; 2 3 import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource; 4 import lombok.SneakyThrows; 5 import org.apache.ibatis.session.SqlSessionFactory; 6 import org.mybatis.spring.SqlSessionFactoryBean; 7 import org.mybatis.spring.SqlSessionTemplate; 8 import org.mybatis.spring.annotation.MapperScan; 9 import org.springframework.beans.factory.annotation.Qualifier; 10 import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean; 11 import org.springframework.context.annotation.Bean; 12 import org.springframework.context.annotation.Configuration; 13 14 import javax.sql.DataSource; 15 16 /** 17 * @Description:订单数据源配置 18 * @author: wph 19 * @Version: 1.0 20 * @date: 2021/1/15 21 */ 22 @Configuration 23 @MapperScan(basePackages = "com.wuwu.cboot.order.mapper",sqlSessionFactoryRef = "orderSqlSessionFactory") 24 public class OrderDataSourceConfig { 25 26 /** 27 * @Description: 配置订单数据源 28 * @author: wph 29 * @date: 2021/1/15 30 * @param orderConfig 31 * @return javax.sql.DataSource 32 */ 33 @Bean("orderDataSource") 34 public DataSource orderDataSource(OrderConfig orderConfig){ 35 //1.创建订单xaDataSource 36 MysqlXADataSource xaDataSource = new MysqlXADataSource(); 37 xaDataSource.setUrl(orderConfig.getUrl()); 38 xaDataSource.setUser(orderConfig.getUsername()); 39 xaDataSource.setPassword(orderConfig.getPassword()); 40 xaDataSource.setPinGlobalTxToPhysicalConnection(true); 41 42 //将xaDataSource注册到全局事务 43 AtomikosDataSourceBean dataSource = new AtomikosDataSourceBean(); 44 dataSource.setXaDataSource(xaDataSource); 45 return dataSource; 46 } 47 48 /** 49 * @Description: 配置session工厂 50 * @author: wph 51 * @date: 2021/1/18 52 * @param dataSource 53 * @return org.apache.ibatis.session.SqlSessionFactory 54 */ 55 @SneakyThrows 56 @Bean("orderSqlSessionFactory") 57 public SqlSessionFactory orderSqlSessionFactory(@Qualifier("orderDataSource") DataSource dataSource){ 58 SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); 59 sqlSessionFactoryBean.setDataSource(dataSource); 60 return sqlSessionFactoryBean.getObject(); 61 } 62 63 /** 64 * @Description: 创建SqlSessionTemplate模板 65 * @author: wph 66 * @date: 2021/1/18 67 * @param sqlSessionFactory 68 * @return org.mybatis.spring.SqlSessionTemplate 69 */ 70 @Bean("orderSqlSessionTemplate") 71 public SqlSessionTemplate orderSqlSessionTemplate(@Qualifier("orderSqlSessionFactory") SqlSessionFactory sqlSessionFactory){ 72 return new SqlSessionTemplate(sqlSessionFactory); 73 } 74 75 }
controller层
1 package com.wuwu.cboot.controller; 2 3 import com.wuwu.cboot.service.MemberService; 4 import org.springframework.beans.factory.annotation.Autowired; 5 import org.springframework.web.bind.annotation.RequestMapping; 6 import org.springframework.web.bind.annotation.RestController; 7 8 /** 9 * @Description:会员控制层 10 * @author: wph 11 * @Version: 1.0 12 * @date: 2020/10/26 13 */ 14 @RestController 15 public class MemberController { 16 17 @Autowired 18 private MemberService memberService; 19 20 /** 21 * @Description: 新增会员用户 22 * @author: wph 23 * @date: 2021/1/20 24 * @param age 25 * @param name 26 * @return java.lang.Object 27 */ 28 @RequestMapping("/saveMember") 29 public Object saveMember(int age,String name){ 30 return memberService.saveMember(age,name); 31 } 32 }
service实现
1 package com.wuwu.cboot.service.impl; 2 3 import com.wuwu.cboot.member.mapper.MemberMapper; 4 import com.wuwu.cboot.order.mapper.OrderMemberMapper; 5 import com.wuwu.cboot.service.MemberService; 6 import org.springframework.stereotype.Service; 7 import org.springframework.transaction.annotation.Transactional; 8 9 import javax.annotation.Resource; 10 11 /** 12 * @Description: 13 * @author: wph 14 * @Version: 1.0 15 * @date: 2021/1/12 16 */ 17 @Service 18 public class MemberServiceImpl implements MemberService { 19 20 @Resource 21 private OrderMemberMapper orderMemberMapper; 22 @Resource 23 private MemberMapper memberMapper; 24 25 @Transactional 26 public String saveMember(int age,String name){ 27 //调用会员接口 28 memberMapper.saveMember(age, name); 29 //调用订单接口 30 int i = orderMemberMapper.saveOrder(123, "dawfa"); 31 int num = 13 / 0; 32 return i == 1 ? "true" : "false"; 33 } 34 }
mapper层
1 package com.wuwu.cboot.member.mapper; 2 3 import org.apache.ibatis.annotations.Insert; 4 import org.apache.ibatis.annotations.Mapper; 5 6 @Mapper 7 public interface MemberMapper { 8 9 @Insert("insert into member (age,name) values(#{age},#{name})") 10 int saveMember(int age, String name);
1 package com.wuwu.cboot.order.mapper; 2 3 import org.apache.ibatis.annotations.Insert; 4 import org.mybatis.spring.annotation.MapperScan; 5 6 @MapperScan 7 public interface OrderMemberMapper { 8 9 @Insert("insert into order_member (order_id,order_code) values(#{orderId},#{orderCode})") 10 int saveOrder(int orderId, String orderCode); 11 }
项目启动类
1 package com.wuwu.cboot; 2 3 import com.wuwu.cboot.config.MemberConfig; 4 import com.wuwu.cboot.config.OrderConfig; 5 import org.springframework.boot.SpringApplication; 6 import org.springframework.boot.autoconfigure.SpringBootApplication; 7 import org.springframework.boot.context.properties.EnableConfigurationProperties; 8 9 @SpringBootApplication 10 @EnableConfigurationProperties({MemberConfig.class, OrderConfig.class}) 11 public class CbootApplication { 12 13 public static void main(String[] args) { 14 SpringApplication.run(CbootApplication.class, args); 15 } 16 17 }
最后附上项目结构目录,如图
ok,直接启动运行即可