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)