一. 相关博客 之前的几篇博客中分析了 Mybatis Plus 的基本用法:
MyBatis Plus 的使用之入门
MyBatis Plus 的自动填充功能
二. 基本环境
本篇博客针对相对复杂的多数据源和动态事务的配置展开介绍,基本环境如下:
SpringBoot : 2.2.4.RELEASE
DruID : 1.1.21
Mybatis Plus : 3.4.0
三. 配置过程 1. 引入依赖
<!-- MyBatis Plus 依赖 --> <dependency> <groupID>com.baomIDou</groupID> <artifactID>mybatis-plus-boot-starter</artifactID> <version>3.4.0</version> </dependency> <!-- druID --> <dependency> <groupID>com.alibaba</groupID> <artifactID>druID-spring-boot-starter</artifactID> <version>1.1.21</version> <scope>compile</scope> </dependency>
2. 修改 yml 配置文件 spring: datasource: druID: db1: # MysqL默认数据库的配置 driverClassname: com.MysqL.cj.jdbc.Driver url: jdbc:MysqL://127.0.0.1:3306/backend_dev?useUnicode=true&characterEnCoding=UTF-8&serverTimezone=GMT%2B8 username: root password: 123456 initialSize: 5 minIDle: 5 maxActive: 20 # 配置获取连接等待超时的时间 maxWait: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 timeBetweenevictionRunsMillis: 60000 # 配置一个连接在池中最小生存的时间,单位是毫秒 minevictableIDleTimeMillis: 300000 valIDationquery: SELECT 1 FROM DUAL testWhileIDle: true testOnBorrow: false testOnReturn: false db2: # MysqL第二个库的配置 driverClassname: com.MysqL.cj.jdbc.Driver url: jdbc:MysqL://127.0.0.1:3306/backend_log_dev?useUnicode=true&characterEnCoding=UTF-8&serverTimezone=GMT%2B8 username: root password: 123456 initialSize: 5 minIDle: 5 maxActive: 20 # 配置获取连接等待超时的时间 maxWait: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 timeBetweenevictionRunsMillis: 60000 # 配置一个连接在池中最小生存的时间,单位是毫秒 minevictableIDleTimeMillis: 300000 valIDationquery: SELECT 1 FROM DUAL testWhileIDle: true testOnBorrow: false testOnReturn: false
3. 数据源枚举 DBTypeEnum /** * @author jichunyang * @description 数据源枚举 */public enum DBTypeEnum { DB1("db1"), DB2("db2"); private String value; DBTypeEnum(String value) { this.value = value; } public String getValue() { return value; }}
4. 标记数据源的注解 MyDataSource /** * @author jichunyang * @description 数据源注解 */@Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@documentedpublic @interface MyDataSource { DBTypeEnum value() default DBTypeEnum.DB1;}
5. 动态数据源管理器 DataSourceContextHolder /** * @author: jichunyang * @description: 设置、获取数据源 * @date: 2020/9/14 17:24 **/public class DataSourceContextHolder { private static final ThreadLocal contextHolder = new ThreadLocal<>(); //开启多个线程,每个线程初始化一个数据源 /** * 设置数据源 * @param dbTypeEnum */ public static voID setDbType(DBTypeEnum dbTypeEnum) { contextHolder.set(dbTypeEnum.getValue()); } /** * 取得当前数据源 * @return */ public static String getDbType() { return (String) contextHolder.get(); } /** * 清除上下文数据 */ public static voID clearDbType() { contextHolder.remove(); }}
6. 动态数据源决策 DynamicdataSource /** * @author: jichunyang * @description: 动态数据源实现 * @date: 2020/9/14 17:22 **/@Slf4jpublic class DynamicdataSource extends AbstractRoutingDataSource { @OverrIDe protected Object determineCurrentLookupKey() { String datasource = DataSourceContextHolder.getDbType(); log.deBUG("当前使用数据源:{}", datasource); return datasource; }}
7. Mybatis Plus 的配置类 MybatisPlusConfig mapper 的目录结构修改如下:
如果使用了 xml 文件进行多表查询,则还需要修改对应的 xml 文件中的路径 namespace:
<?xml version="1.0" enCoding="UTF-8" ?><!DOCTYPE mapper PUBliC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!-- 此处与接口类地址对应 --><mapper namespace="com.system.domain.mapper.db1.DataMapper"> <select ID="pageDatas" resultType="map"> ... <!-- 对应的pageDatas查询sql --> </select></mapper>
mapper 所在目录为 com.system.domain.mapper,因此 @MapperScan 需要扫描的路径为mapper下的 db1 和 db2,配置如下:
com.system.domain.mapper.db*
MybatisPlusConfig 配置类
/** * @author jichunyang * @description mybatis plus配置 */@EnableTransactionManagement //开启事务@Configuration@MapperScan("com.system.domain.mapper.db*")public class MybatisPlusConfig { /** * 分页插件 */ @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } @Bean(name = "db1") @ConfigurationPropertIEs(prefix = "spring.datasource.druID.db1") public DataSource db1() { return DruIDDataSourceBuilder.create().build(); } @Bean(name = "db2") @ConfigurationPropertIEs(prefix = "spring.datasource.druID.db2") public DataSource db2() { return DruIDDataSourceBuilder.create().build(); } /** * 动态数据源配置 * * @return */ @Bean(name = "multipleDataSource") @Primary public DataSource multipleDataSource(@QualifIEr("db1") DataSource db1, @QualifIEr("db2") DataSource db2) { DynamicdataSource dynamicdataSource = new DynamicdataSource(); Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put(DBTypeEnum.DB1.getValue(), db1); targetDataSources.put(DBTypeEnum.DB2.getValue(), db2); dynamicdataSource.setTargetDataSources(targetDataSources); // 程序默认数据源,根据程序调用数据源频次,把常调用的数据源作为默认 dynamicdataSource.setDefaultTargetDataSource(db1); return dynamicdataSource; } @Bean("sqlSessionFactory") public sqlSessionFactory sqlSessionFactory() throws Exception { MybatissqlSessionfactorybean sqlSessionFactory = new MybatissqlSessionfactorybean(); sqlSessionFactory.setDataSource(multipleDataSource(db1(), db2())); // 设置默认需要扫描的 xml 文件 sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml")); //其他配置项 MybatisConfiguration configuration = new MybatisConfiguration(); configuration.setJdbcTypeForNull(JdbcType.NulL); // 驼峰和下划线转换 configuration.setMapUnderscoreToCamelCase(true); configuration.setCacheEnabled(false); configuration.setCallSettersOnNulls(true); sqlSessionFactory.setConfiguration(configuration); sqlSessionFactory.setTypeAliasesPackage("com.intyt.jcyy.system.domain"); // 数据库查询结果驼峰式返回 sqlSessionFactory.setobjectWrapperFactory(new MybatisMapWrapperFactory()); // 添加分页功能 sqlSessionFactory.setPlugins(new Interceptor[]{ paginationInterceptor() }); // 实现自动填充功能 sqlSessionFactory.setGlobalConfig(globalConfiguration()); return sqlSessionFactory.getobject(); } @Bean(name = "multipleTransactionManager") @Primary public DataSourceTransactionManager multipleTransactionManager(@QualifIEr("multipleDataSource") DataSource dataSource) { // 动态事务配置 return new DataSourceTransactionManager(dataSource); } @Bean public GlobalConfig globalConfiguration() { // 自动填充创建时间和更新时间(MyMetaObjectHandler的实现参考 “MyBatis Plus 的自动填充功能” 博客) GlobalConfig conf = new GlobalConfig(); conf.setMetaObjectHandler(new MyMetaObjectHandler()); return conf; }
8. 使用AOP实现数据源的动态设置 /** * @author: jichunyang * @description: aop实现数据源切换 * @date: 2020/9/14 17:29 **/@Component@Order(value = -100)@Slf4j@Aspectpublic class DataSourceAspect { @Before("execution(* com.system.service.impl.*.*(..)) && @annotation(com.common.annotation.MyDataSource)") public voID before(JoinPoint joinPoint) { // execution 中配置的是服务实现类 & MyDataSource的包路径 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getmethod(); MyDataSource myDataSource = null; // 判断方法上的注解 if (method.isAnnotationPresent(MyDataSource.class)) { myDataSource = method.getAnnotation(MyDataSource.class); DataSourceContextHolder.setDbType(myDataSource.value()); } else if (method.getDeclaringClass().isAnnotationPresent(MyDataSource.class)) { //其次判断类上的注解 myDataSource = method.getDeclaringClass().getAnnotation(MyDataSource.class); DataSourceContextHolder.setDbType(myDataSource.value()); } if (myDataSource != null) { log.info("注解方式选择数据源---" + myDataSource.value().getValue()); } } /** * 服务类的方法结束后,会清除数据源,此时会变更为默认的数据源 **/ @After("execution(* com.system.service.impl.*.*(..)) && @annotation(com.common.annotation.MyDataSource)") public voID after(JoinPoint point){ // execution 中配置的是服务实现类 & MyDataSource的包路径 DataSourceContextHolder.clearDbType(); }}
9.实际使用:在ServiceImpl 类中使用注解进行标识 即所有被标识了以下事务注解 @Transactional 和数据源注解 @MyDataSource 的service实现类都可以实现动态数据源和事务的切换:
@MyDataSource(DBTypeEnum.DB1)@Transactional
10.测试结果 如果在按照以上步骤配置后,发现数据源切换不生效,可检查是否是 @Transactional 注解失效,可参考以下博客:
Spring的声明式事务@Transactional注解的6种失效场景
欢迎关注我的公众号,用讲故事的方式学技术。
这里有脑洞大开的奇葩故事,也有温暖文艺的心灵感悟。
技术知识,也可以很有趣。
以上是内存溢出为你收集整理的SpringBoot + Mybatis Plus + Druid 实现多数据源切换和动态事务全部内容,希望文章能够帮你解决SpringBoot + Mybatis Plus + Druid 实现多数据源切换和动态事务所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)