# springboot_mybatis_mybatis-plus多数据源
**Repository Path**: hugo123/sssmp
## Basic Information
- **Project Name**: springboot_mybatis_mybatis-plus多数据源
- **Description**: springboot 集成 mybatis plus 多数据源
- **Primary Language**: Java
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 2
- **Created**: 2021-07-27
- **Last Updated**: 2021-07-27
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
[toc]
# springboot2+mybatis plus多数据源
+ 关键步骤如下
## 在application.properties中配置数据源
```
spring.datasource.datasource1.username=root
spring.datasource.datasource1.password=cstcb
spring.datasource.datasource1.jdbc-url=jdbc:mysql://localhost:3306/datasource1?serverTimezone=Asia/Shanghai&useSSL=false&autoReconnect=true&characterEncoding=utf8&allowMultiQueries=true
spring.datasource.datasource1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.datasource2.username=root
spring.datasource.datasource2.password=cstcb
spring.datasource.datasource2.jdbc-url=jdbc:mysql://localhost:3306/datasource2?serverTimezone=Asia/Shanghai&useSSL=false&autoReconnect=true&characterEncoding=utf8&allowMultiQueries=true
spring.datasource.datasource2.driver-class-name=com.mysql.cj.jdbc.Driver
```
## pom.xml配置mybatis-plus
```
com.baomidou
mybatis-plus-boot-starter
3.1.0
```
## 为各数据源配置指定mapper路径,注意这里要用mybatis-plus的配置代替默认mybatis的MybatisConfiguration
```
// 数据源1
@Configuration
//配置mybatis的接口类放的地方
@MapperScan(basePackages = "dev.gu.study.bean.datasource1", sqlSessionFactoryRef = "ds1SqlSessionFactory")
public class DataSourceConfig1 {
// 将这个对象放入Spring容器中
@Bean(name = "ds1DataSource")
// 表示这个数据源是默认数据源
@Primary
// 读取application.properties中的配置参数映射成为一个对象
// prefix表示参数的前缀
@ConfigurationProperties(prefix = "spring.datasource.datasource1")
public DataSource getDateSource1() {
return DataSourceBuilder.create().build();
}
@Bean(name = "ds1SqlSessionFactory")
// 表示这个数据源是默认数据源
@Primary
// @Qualifier表示查找Spring容器中名字为ds1DataSource的对象
public MybatisSqlSessionFactoryBean iProcessSqlSessionFactory(
@Qualifier("ds1DataSource") DataSource datasource) throws Exception {
MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(getDateSource1());
MybatisConfiguration configuration = new MybatisConfiguration();
configuration.setMapUnderscoreToCamelCase(false);
sqlSessionFactoryBean.setConfiguration(configuration);
// sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/mapper/*Mapper.xml"));
return sqlSessionFactoryBean;
}
@Bean("ds1SqlSessionTemplate")
// 表示这个数据源是默认数据源
@Primary
public SqlSessionTemplate test1sqlsessiontemplate(
@Qualifier("ds1SqlSessionFactory") SqlSessionFactory sessionfactory) {
return new SqlSessionTemplate(sessionfactory);
}
}
// 数据源2
@Configuration
//配置mybatis的接口类放的地方
@MapperScan(basePackages = "dev.gu.study.bean.datasource2", sqlSessionFactoryRef = "ds2SqlSessionFactory")
public class DataSourceConfig2 {
// 将这个对象放入Spring容器中
@Bean(name = "ds2DataSource")
@ConfigurationProperties(prefix = "spring.datasource.datasource2")
public DataSource getDateSource2() {
return DataSourceBuilder.create().build();
}
@Bean(name = "ds2SqlSessionFactory")
// 表示这个数据源是默认数据源
@Primary
// @Qualifier表示查找对象
public MybatisSqlSessionFactoryBean cstSqlSessionFactory(@Qualifier("ds2DataSource") DataSource datasource) throws Exception {
MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(getDateSource2());
//这里如果用mybatis plus的话,要用mybatis-plus的configuration
MybatisConfiguration configuration = new MybatisConfiguration();
configuration.setMapUnderscoreToCamelCase(false);
sqlSessionFactoryBean.setConfiguration(configuration);
// sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/mapper/*Mapper.xml"));
return sqlSessionFactoryBean;
}
@Bean("ds2SqlSessionTemplate")
public SqlSessionTemplate test1sqlsessiontemplate(
@Qualifier("ds2SqlSessionFactory") SqlSessionFactory sessionfactory) {
return new SqlSessionTemplate(sessionfactory);
}
}
```
## 将bean和mapper对象放在数据源指定的子包下
```
依数据源的配置,datasource1将管理dev.gu.study.bean.datasource1.*的包下的数据连接
datasource2 将管理dev.gu.study.bean.datasource2.*的包下的数据连接
```
## 以上步骤为配置多数据源,但仅仅这么配置,只能每次启用一个数据源,如果需要多数据源同时commit或rollback,需要额外一些配置
### 声明一个runtime注解,用于标注所有要开启的数据源事务
```
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
public @interface MultipleTx {
String[] value() default {};
}
```
### 在controller层或service层使用此注解标注要开启的事务
```
//spring 跨数据库事务 这里的名字就是DataSourceConfig中的TransactionManager的注册名字,表示这些事务一起commit或rollback
@MultipleTx({ "cstTxManager", "iTxManager" })
@RestController
@RequestMapping("/test")
public class TestController {
}
```
### 使用controllerAspect或ServiceAspect进行环切,处理事务的开启或关闭
```
package dev.gu.study.aspect;
import java.lang.reflect.Method;
import java.util.Stack;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
@Aspect
@Component
public class ControllerAspect {
@Autowired
private ApplicationContext context;
public Object runLog(ProceedingJoinPoint pjp) {
long beginTime = System.currentTimeMillis();
Object result = null;
Throwable ex = null;
boolean exceptionLog = false;
Stack dataSourceTransactionManagerStack = new Stack<>();
Stack transactionStatuStack = new Stack<>();
boolean tx = false;
try {
MultipleTx classTx = pjp.getTarget().getClass().getAnnotation(MultipleTx.class);
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
MultipleTx methodTx = method.getAnnotation(MultipleTx.class);
MultipleTx[] mtxes = { classTx, methodTx };
tx = openTransaction(dataSourceTransactionManagerStack, transactionStatuStack, mtxes);
result = pjp.proceed();
if (tx) {
commit(dataSourceTransactionManagerStack, transactionStatuStack);
}
} catch (Throwable e) {
// TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
if (tx) {
rollback(dataSourceTransactionManagerStack, transactionStatuStack);
}
ex = e;
e.printStackTrace();
exceptionLog = true;
} finally {
}
saveLog(pjp, beginTime, exceptionLog, ex);
return result;
}
@Around("@within(org.springframework.stereotype.Controller)")
public Object aroundController(ProceedingJoinPoint pjp) {
return runLog(pjp);
}
@Around("@within(org.springframework.web.bind.annotation.RestController)")
public Object aroundRestController(ProceedingJoinPoint pjp) {
return runLog(pjp);
}
/**
* 保存日志
*
* @param joinPoint
* @param time
*/
private void saveLog(ProceedingJoinPoint pjp, long beginTime, boolean exceptionLog, Throwable ex) {
// long endTime = System.currentTimeMillis();
// MethodSignature signature = (MethodSignature) pjp.getSignature();
// Method method = signature.getMethod();
// String clzName = pjp.getTarget().getClass().getName();
// String methodName = method.getName();
// StringBuilder loginfo = new StringBuilder();
// loginfo.append("class:").append(clzName).append("\r\n").append("method:").append(methodName).append("\r\n")
// .append("params:").append("\r\n");
// Object[] args = pjp.getArgs();
// if (args != null) {
// for (Object o : args) {
// if (o instanceof Object[]) {
// loginfo.append(Arrays.asList((Object[]) o));
// } else {
// loginfo.append(o);
// }
// loginfo.append("\r\n");
// }
// }
// if (exceptionLog) {
// loginfo.append("exception at \r\n").append(Strings.parseException(ex));
// log.error(aroundLog(loginfo));
// } else {
// long runTime = endTime - beginTime;
// if (slowTime > 0 && runTime > slowTime) {
// loginfo.append("starttime:" + LocalDateTimes.format(beginTime)).append("\r\n");
// loginfo.append("finishtime:" + LocalDateTimes.format(beginTime)).append("\r\n");
// log.warn(aroundLog(loginfo));
// } else {
// log.info(aroundLog(loginfo));
// }
// }
}
/**
* @author guweichao 2019-03-13
* @desc 为日志信息添加统一前后缀
* @param loginfo
* @return
*/
private String aroundLog(StringBuilder loginfo) {
StringBuilder logstr = new StringBuilder();
return logstr.append("\r\n").append("------------------------log.start--------------------------")
.append("\r\n").append(loginfo).append("------------------------log.finish--------------------------")
.append("\r\n").toString();
}
/**
* 开启事务处理方法
*
* @param dataSourceTransactionManagerStack
* @param transactionStatuStack
* @param multiTransactional
* @return
*/
private boolean openTransaction(Stack dataSourceTransactionManagerStack,
Stack transactionStatuStack, MultipleTx[] mtxes) {
boolean[] re = { false };
if (mtxes != null) {
for (MultipleTx mtx : mtxes) {
if (mtx != null) {
String[] transactionMangerNames = mtx.value();
if (transactionMangerNames != null) {
for (String tname : transactionMangerNames) {
re[0] = true;
DataSourceTransactionManager dataSourceTransactionManager = (DataSourceTransactionManager) context.getBean(tname);
TransactionStatus transactionStatus = dataSourceTransactionManager
.getTransaction(new DefaultTransactionDefinition());
transactionStatuStack.push(transactionStatus);
dataSourceTransactionManagerStack.push(dataSourceTransactionManager);
}
}
}
}
}
return re[0];
}
/**
* 提交处理方法
*
* @param dataSourceTransactionManagerStack
* @param transactionStatuStack
*/
private void commit(Stack dataSourceTransactionManagerStack,
Stack transactionStatuStack) {
while (!dataSourceTransactionManagerStack.isEmpty()) {
dataSourceTransactionManagerStack.pop().commit(transactionStatuStack.pop());
}
}
/**
* 回滚处理方法
*
* @param dataSourceTransactionManagerStack
* @param transactionStatuStack
*/
private void rollback(Stack dataSourceTransactionManagerStack,
Stack transactionStatuStack) {
while (!dataSourceTransactionManagerStack.isEmpty()) {
dataSourceTransactionManagerStack.pop().rollback(transactionStatuStack.pop());
}
}
}
```
## 详细代码可参考附件