MyBatis多数据源及MyBatis+atomikos分布式事务

本篇我们来讲解MyBatis的多数据实现,与SpringDataJPA多数据源实现方式一样,仍然采取分包策略。

MyBatis多数据源

还是先来配置application.yml文件:

spring:
  jackson:
 date-format: yyyy-MM-dd HH:mm:ss
 time-zone: GMT+8

  datasource:
 family:
   driver-class-name: com.mysql.cj.jdbc.Driver
   url: jdbc:mysql://localhost:3306/Family?useUnicode=true&characterEncoding=utf-8&useSSL=false
   username: root
   password: 123456

 family2:
 driver-class-name: com.mysql.cj.jdbc.Driver
 url: jdbc:mysql://localhost:3306/Family2?useUnicode=true&characterEncoding=utf-8&useSSL=false
 username: root
 password: 1234561234567891011121314151617复制代码类型:[java]

将FamilyDemoApplication中的@MapperScan注解注释掉。

我们要在config文件夹下添加两个数据源配置:

第一个数据源配置:

package com.javafamily.familydemo.config;import org.apache.ibatis.session.SqlSessionFactory;import org.mybatis.spring.SqlSessionFactoryBean;import org.mybatis.spring.SqlSessionTemplate;import org.mybatis.spring.annotation.MapperScan;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.boot.jdbc.DataSourceBuilder;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Primary;import org.springframework.core.io.support.PathMatchingResourcePatternResolver;import org.springframework.jdbc.datasource.DataSourceTransactionManager;import javax.sql.DataSource;@Configuration@MapperScan(basePackages = "com.javafamily.familydemo.generator.db",
  sqlSessionTemplateRef = "familySqlSessionTemplate")public class FamilyDataSourceConfig { @Bean(name = "familyDataSource")
 @ConfigurationProperties(prefix = "spring.datasource.family")
 @Primary
 public DataSource familyDataSource() {  return DataSourceBuilder.create().build();
 } @Bean(name = "familySqlSessionFactory")
 @Primary
 public SqlSessionFactory primarySqlSessionFactory(   @Qualifier("familyDataSource") DataSource dataSource)
   throws Exception {
  SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
  bean.setDataSource(dataSource);  //设置XML文件存放位置
  bean.setMapperLocations(new PathMatchingResourcePatternResolver()
 .getResources("classpath:generator/db/*.xml"));  return bean.getObject();
 } @Bean(name = "familyTransactionManager")
 @Primary
 public DataSourceTransactionManager familyTransactionManager(   @Qualifier("familyDataSource") DataSource dataSource) {  return new DataSourceTransactionManager(dataSource);
 } @Bean(name = "familySqlSessionTemplate")
 @Primary
 public SqlSessionTemplate familySqlSessionTemplate(   @Qualifier("familySqlSessionFactory") SqlSessionFactory sqlSessionFactory)
   throws Exception {  return new SqlSessionTemplate(sqlSessionFactory);
 }
}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657复制代码类型:[java]

第二个数据源配置:

package com.javafamily.familydemo.config;import org.apache.ibatis.session.SqlSessionFactory;import org.mybatis.spring.SqlSessionFactoryBean;import org.mybatis.spring.SqlSessionTemplate;import org.mybatis.spring.annotation.MapperScan;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.boot.jdbc.DataSourceBuilder;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Primary;import org.springframework.core.io.support.PathMatchingResourcePatternResolver;import org.springframework.jdbc.datasource.DataSourceTransactionManager;import javax.sql.DataSource;@Configuration@MapperScan(basePackages = "com.javafamily.familydemo.generator.db2",
  sqlSessionTemplateRef = "family2SqlSessionTemplate")public class Family2DataSourceConfig { @Bean(name = "family2DataSource")
 @ConfigurationProperties(prefix = "spring.datasource.family2")
 public DataSource family2DataSource() {  return DataSourceBuilder.create().build();
 } @Bean(name = "family2SqlSessionFactory")
 public SqlSessionFactory family2SqlSessionFactory(   @Qualifier("family2DataSource") DataSource dataSource)
   throws Exception {
  SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
  bean.setDataSource(dataSource);
  bean.setMapperLocations(new PathMatchingResourcePatternResolver()
 .getResources("classpath:generator/db2/*.xml"));  return bean.getObject();
 } @Bean(name = "family2TransactionManager")
 public DataSourceTransactionManager family2TransactionManager(   @Qualifier("family2DataSource") DataSource dataSource) {  return new DataSourceTransactionManager(dataSource);
 } @Bean(name = "family2SqlSessionTemplate")
 public SqlSessionTemplate family2SqlSessionTemplate(   @Qualifier("family2SqlSessionFactory") SqlSessionFactory sqlSessionFactory)
   throws Exception {  return new SqlSessionTemplate(sqlSessionFactory);
 }
}12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152复制代码类型:[java]

完成数据源的配置后,PetsServiceImpl进行改写,向两个数据库中添加数据。

@Resource
 protected Mapper dozerMapper; @Resource
 private PetsDao petsDao; @Resource
 private DoctorDao doctorDao; @Override
 public PetsVO savePets(PetsVO pets) {
  Pets petsPO = dozerMapper.map(pets, Pets.class);
  petsDao.insert(petsPO);

  Doctor doctor = new Doctor();
  doctor.setName("Java");
  doctor.setTitle("实习");
  doctorDao.insert(doctor);  return pets;
 }12345678910111213141516171819202122复制代码类型:[java]

完成代码改写后,执行代码,并在postman中进行测试:

数据分别传入两个数据库中:

MyBatis+atomikos分布式事务

与之前讲解的分布式事务一样,在PetsServiceImpl.java中的savePets方法中添加@Transactional注解和异常代码。

@Resource
 protected Mapper dozerMapper; @Resource
 private PetsDao petsDao; @Resource
 private DoctorDao doctorDao; @Override
 @Transactional
 public PetsVO savePets(PetsVO pets) {
  Pets petsPO = dozerMapper.map(pets, Pets.class);
  petsDao.insert(petsPO);

  Doctor doctor = new Doctor();
  doctor.setName("Java");
  doctor.setTitle("实习");
  doctorDao.insert(doctor);  // 添加异常
  int num = 1 / 0;  return pets;
 }12345678910111213141516171819202122232425复制代码类型:[java]

由于没有添加分布式事务,所以在执行代码报错后,第一个数据库不会被插入数据,第二个数据库的数据会正常插入。

maven依赖已经在之前讲解的分布式事务文章中导入了,这里不再重复导入。

改写application.yml,实现双数据源配置:

spring:
  jackson:
 date-format: yyyy-MM-dd HH:mm:ss
 time-zone: GMT+8
  datasource:
 family:
   xaDataSourceClassName: com.mysql.cj.jdbc.MysqlXADataSource
   xaProperties:
  url: jdbc:mysql://localhost:3306/Family?useUnicode=true&characterEncoding=utf-8&useSSL=false
  user: root
  password: 123456
   exclusiveConnectionMode: true
   minPoolSize: 2
   maxPoolSize: 10
   testQuery: SELECT 1 from dual

 family2:
   xaDataSourceClassName: com.mysql.cj.jdbc.MysqlXADataSource
   xaProperties:
  url: jdbc:mysql://localhost:3306/Family2?useUnicode=true&characterEncoding=utf-8&useSSL=false
  user: root
  password: 123456
   exclusiveConnectionMode: true
   minPoolSize: 2
   maxPoolSize: 10
   testQuery: SELECT 1 from dual1234567891011121314151617181920212223242526复制代码类型:[java]

更改两个数据源配置:

第一个数据源配置:

package com.javafamily.familydemo.config;import org.apache.ibatis.session.SqlSessionFactory;import org.mybatis.spring.SqlSessionFactoryBean;import org.mybatis.spring.SqlSessionTemplate;import org.mybatis.spring.annotation.MapperScan;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Primary;import org.springframework.core.io.support.PathMatchingResourcePatternResolver;import javax.sql.DataSource;@Configuration@MapperScan(basePackages = "com.javafamily.familydemo.generator.db",
  sqlSessionTemplateRef = "familySqlSessionTemplate")public class FamilyDataSourceConfig { @Bean(name = "familyDataSource")
 @ConfigurationProperties(prefix = "spring.datasource.family")
 @Primary
 public DataSource familyDataSource() {  return new AtomikosDataSourceBean();
 } @Bean(name = "familySqlSessionFactory")
 @Primary
 public SqlSessionFactory primarySqlSessionFactory(   @Qualifier("familyDataSource") DataSource dataSource)
   throws Exception {
  SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
  bean.setDataSource(dataSource);  //设置XML文件存放位置
  bean.setMapperLocations(new PathMatchingResourcePatternResolver()
 .getResources("com/javafamily/familydemo/generator/db/*.xml"));  return bean.getObject();
 }// @Bean(name = "familyTransactionManager")// @Primary// public DataSourceTransactionManager familyTransactionManager(//   @Qualifier("familyDataSource") DataSource dataSource) {//  return new DataSourceTransactionManager(dataSource);// }

 @Bean(name = "familySqlSessionTemplate")
 @Primary
 public SqlSessionTemplate familySqlSessionTemplate(   @Qualifier("familySqlSessionFactory") SqlSessionFactory sqlSessionFactory)
   throws Exception {  return new SqlSessionTemplate(sqlSessionFactory);
 }
}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556复制代码类型:[java]

第二个数据源配置:

package com.javafamily.familydemo.config;import org.apache.ibatis.session.SqlSessionFactory;import org.mybatis.spring.SqlSessionFactoryBean;import org.mybatis.spring.SqlSessionTemplate;import org.mybatis.spring.annotation.MapperScan;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.io.support.PathMatchingResourcePatternResolver;import javax.sql.DataSource;@Configuration@MapperScan(basePackages = "com.javafamily.familydemo.generator.db2",
  sqlSessionTemplateRef = "family2SqlSessionTemplate")public class Family2DataSourceConfig { @Bean(name = "family2DataSource")
 @ConfigurationProperties(prefix = "spring.datasource.family2")
 public DataSource family2DataSource() {  return new AtomikosDataSourceBean();
 } @Bean(name = "family2SqlSessionFactory")
 public SqlSessionFactory family2SqlSessionFactory(   @Qualifier("family2DataSource") DataSource dataSource)
   throws Exception {
  SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
  bean.setDataSource(dataSource);
  bean.setMapperLocations(new PathMatchingResourcePatternResolver()
 .getResources("com/javafamily/familydemo/generator/db2/*.xml"));  return bean.getObject();
 }// @Bean(name = "family2TransactionManager")// public DataSourceTransactionManager family2TransactionManager(//   @Qualifier("family2DataSource") DataSource dataSource) {//  return new DataSourceTransactionManager(dataSource);// }

 @Bean(name = "family2SqlSessionTemplate")
 public SqlSessionTemplate family2SqlSessionTemplate(   @Qualifier("family2SqlSessionFactory") SqlSessionFactory sqlSessionFactory)
   throws Exception {  return new SqlSessionTemplate(sqlSessionFactory);
 }
}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950复制代码类型:[java]

自动生成的代码,分别存放于db和db2两个文件夹:

由于更改了XML文件的存放位置所以移动文件目录后启动项目,如果发现target目录下没有加载到.xml文件时,需要在pom.xml下的<bulid></build>中加入以下配置:

<resources>
 <resource>
 <directory>src/main/java</directory>
 <includes>
 <include>**/*.xml</include>
 </includes>
 </resource>
 </resources>12345678复制代码类型:[python]

在config文件夹中添加统一事务管理器:

package com.javafamily.familydemo.config;import com.atomikos.icatch.jta.UserTransactionImp;import com.atomikos.icatch.jta.UserTransactionManager;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.DependsOn;import org.springframework.transaction.PlatformTransactionManager;import org.springframework.transaction.annotation.EnableTransactionManagement;import org.springframework.transaction.jta.JtaTransactionManager;import javax.transaction.TransactionManager;import javax.transaction.UserTransaction;@Configuration@EnableTransactionManagementpublic class XATransactionManagerConfig {

 // User事务
 @Bean(name = "userTransaction") public UserTransaction userTransaction() throws Throwable {  UserTransactionImp userTransactionImp = new UserTransactionImp();  userTransactionImp.setTransactionTimeout(10000);  return userTransactionImp;
 }

 // 分布式事务
 @Bean
 public TransactionManager atomikosTransactionManager() throws Throwable {  UserTransactionManager userTransactionManager = new UserTransactionManager();  userTransactionManager.setForceShutdown(false);  return userTransactionManager;
 }

 // 事务管理器
 @Bean(name = "transactionManager")
 @DependsOn({"userTransaction", "atomikosTransactionManager"}) public PlatformTransactionManager transactionManager() throws Throwable {  return new JtaTransactionManager(userTransaction(), atomikosTransactionManager());
 }

}123456789101112131415161718192021222324252627282930313233343536373839404142复制代码类型:[python]

执行代码,向两个数据库中添加数据后,postman测试:

再查看数据库:

两个数据库均没有数据被插入,至此分布式事务完成。

(0)

相关推荐