上一篇文章《SpringBoot实现多数据源(四)【集成多个 Mybatis 框架】》
在多数据源下,由于涉及到数据库的多个读写。一旦发生异常可能会导致数据不一致的情况,在这种情况希望使用事务进行回退
但是 Spring 的声明式事务在一次请求线程中只能使用一个数据源进行控制
对于多源数据库来讲:
一个方法开启2个事务
1)编程式事务
package com.vinjcent.config.mybatis;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.apache.ibatis.logging.stdout.StdOutImpl;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
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 org.springframework.transaction.support.TransactionTemplate;
import javax.sql.DataSource;
/**
* 读数据源配置
* 1. 指定扫描mapper接口包
* 2. 指定使用rSqlSessionFactory是哪个
*/
@Configuration
@MapperScan(basePackages = "com.vinjcent.mapper.read", sqlSessionFactoryRef = "rSqlSessionFactory")
public class RMybatisConfiguration {
// readDataSource(读数据源)
@Bean(name = "readDatasource")
@ConfigurationProperties(prefix = "spring.datasource.read")
public DataSource readDatasource() {
// 底层会自动拿到spring.datasource中的配置,创建一个DruidDataSource
return DruidDataSourceBuilder.create().build();
}
// SqlSessionFactory(ibatis会话工厂)
@Bean
@Primary
public SqlSessionFactory rSqlSessionFactory(@Qualifier("readDatasource") DataSource dataSource) throws Exception {
final SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
sqlSessionFactory.setDataSource(dataSource);
sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath:com/vinjcent/mapper/read/*.xml"));
/*主库设置sql控制台打印*/
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configuration.setLogImpl(StdOutImpl.class);
sqlSessionFactory.setConfiguration(configuration);
sqlSessionFactory.setTypeAliasesPackage("com.vinjcent.pojo");
return sqlSessionFactory.getObject();
}
// TransactionManager(事务管理者)
@Bean
public DataSourceTransactionManager rTransactionManager() {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(readDatasource());
return transactionManager;
}
// TransactionTemplate(事务模板)
@Bean
public TransactionTemplate rTransactionTemplate() {
return new TransactionTemplate(rTransactionManager());
}
}
package com.vinjcent.config.mybatis;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.apache.ibatis.logging.stdout.StdOutImpl;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
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 org.springframework.transaction.support.TransactionTemplate;
import javax.sql.DataSource;
/**
* 写数据源配置
* 1. 指定扫描mapper接口包
* 2. 指定使用wSqlSessionFactory是哪个
*/
@Configuration
@MapperScan(basePackages = "com.vinjcent.mapper.write", sqlSessionFactoryRef = "wSqlSessionFactory")
public class WMybatisConfiguration {
// writeDataSource(写数据源)
@Bean(name = "writeDatasource")
@ConfigurationProperties(prefix = "spring.datasource.write")
public DataSource writeDatasource() {
// 底层会自动拿到spring.datasource中的配置,创建一个DruidDataSource
return DruidDataSourceBuilder.create().build();
}
// SqlSessionFactory(ibatis会话工厂)
@Bean
@Primary
public SqlSessionFactory wSqlSessionFactory(@Qualifier("writeDatasource") DataSource dataSource) throws Exception {
final SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
sqlSessionFactory.setDataSource(dataSource);
sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath:com/vinjcent/mapper/write/*.xml"));
/* 主库设置sql控制台打印 */
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configuration.setLogImpl(StdOutImpl.class);
sqlSessionFactory.setConfiguration(configuration);
sqlSessionFactory.setTypeAliasesPackage("com.vinjcent.pojo");
return sqlSessionFactory.getObject();
}
// TransactionManager(事务管理者)
@Bean
@Primary
public DataSourceTransactionManager wTransactionManager() {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(writeDatasource());
return transactionManager;
}
// TransactionTemplate(事务模板)
@Bean
public TransactionTemplate wTransactionTemplate() {
return new TransactionTemplate(wTransactionManager());
}
}
package com.vinjcent.service.impl;
import com.vinjcent.mapper.read.RPeopleMapper;
import com.vinjcent.mapper.write.WPeopleMapper;
import com.vinjcent.pojo.People;
import com.vinjcent.service.PeopleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;
import java.util.List;
@Service
public class PeopleServiceImpl implements PeopleService {
// 读mapper
private final RPeopleMapper rPeopleMapper;
// 写mapper
private final WPeopleMapper wPeopleMapper;
// 读事务模板
private final TransactionTemplate rTransactionTemplate;
// 写事务模板
private final TransactionTemplate wTransactionTemplate;
@Autowired
public PeopleServiceImpl(RPeopleMapper rPeopleMapper, WPeopleMapper wPeopleMapper, TransactionTemplate rTransactionTemplate, TransactionTemplate wTransactionTemplate) {
this.rPeopleMapper = rPeopleMapper;
this.wPeopleMapper = wPeopleMapper;
this.rTransactionTemplate = rTransactionTemplate;
this.wTransactionTemplate = wTransactionTemplate;
}
@Override
public List<People> list() {
return rPeopleMapper.list();
}
@Override
public boolean save(People people) {
return wPeopleMapper.save(people);
}
// 从库保存
public boolean rSave(People people) {
return rPeopleMapper.save(people);
}
// 主库保存
public boolean wSave(People people) {
return wPeopleMapper.save(people);
}
// 主从库保存
@Override
public void saveAll(People people) {
// 写事务模板
wTransactionTemplate.execute(wStatus -> {
// 读事务模板
rTransactionTemplate.execute(rStatus -> {
try {
rSave(people);
wSave(people);
// 模拟异常
int a = 1 /0;
} catch (Exception e) {
e.printStackTrace();
// 出现异常回滚"写"事务
wStatus.setRollbackOnly();
// 出现异常回滚"读"事务
rStatus.setRollbackOnly();
return false;
}
return true;
});
return true;
});
}
}
package com.vinjcent.controller;
import com.vinjcent.pojo.People;
import com.vinjcent.service.PeopleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("people")
public class PeopleController {
private final PeopleService peopleService;
@Autowired
public PeopleController(PeopleService peopleService) {
this.peopleService = peopleService;
}
@GetMapping("/list")
public List<People> getAllPeople() {
//...
}
@GetMapping("/insert")
public String addPeople() {
//...
}
// 添加位置
@GetMapping("/save")
public String addPeopleForWriteAndRead() {
peopleService.saveAll(new People("ReadAndWrite"));
return "读写库添加成功~";
}
}
2)声明式事务
修改Service层
package com.vinjcent.service.impl;
import com.vinjcent.mapper.read.RPeopleMapper;
import com.vinjcent.mapper.write.WPeopleMapper;
import com.vinjcent.pojo.People;
import com.vinjcent.service.PeopleService;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import java.util.List;
@Service
public class PeopleServiceImpl implements PeopleService {
// 读mapper
private final RPeopleMapper rPeopleMapper;
// 写mapper
private final WPeopleMapper wPeopleMapper;
// 读事务模板
private final TransactionTemplate rTransactionTemplate;
// 写事务模板
private final TransactionTemplate wTransactionTemplate;
@Autowired
public PeopleServiceImpl(RPeopleMapper rPeopleMapper, WPeopleMapper wPeopleMapper, TransactionTemplate rTransactionTemplate, TransactionTemplate wTransactionTemplate) {
this.rPeopleMapper = rPeopleMapper;
this.wPeopleMapper = wPeopleMapper;
this.rTransactionTemplate = rTransactionTemplate;
this.wTransactionTemplate = wTransactionTemplate;
}
@Override
public List<People> list() {
return rPeopleMapper.list();
}
@Override
public boolean save(People people) {
return wPeopleMapper.save(people);
}
// 从库保存
public boolean rSave(People people) {
return rPeopleMapper.save(people);
}
// 主库保存
public boolean wSave(People people) {
return wPeopleMapper.save(people);
}
// 主从库保存
@Transactional("wTransactional")
@Override
public void saveAll(People people) {
// 获取当前的service代理类对象,需要在主启动类开启@EnableAspectJAutoProxy(exposeProxy = true),暴露代理对象
PeopleService peopleService = (PeopleService) AopContext.currentProxy();
peopleService.saveAllR(people);
}
@Transactional(value = "rTransactional")
@Override
public void saveAllR(People people) {
wSave(people);
rSave(people);
int a = 1 / 0;
}
}