# 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()); } } } ``` ## 详细代码可参考附件