Fork me on GitLab

spring-boot+mybatis-plus多数据源切换

配置application信息

#多数据源配置如下:
spring.datasource.db1.jdbc-url=jdbc:mysql://localhost:3306/db1?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
spring.datasource.db1.username=root
spring.datasource.db1.password=123456
spring.datasource.db1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.db1.minimum-idle=1
spring.datasource.db1.maximum-pool-size=3

spring.datasource.db2.jdbc-url=jdbc:mysql://slocalhost:3307/db2?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
spring.datasource.db2.username=root
spring.datasource.db2.password=123456
spring.datasource.db2.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.db2.minimum-idle=1
spring.datasource.db2.maximum-pool-size=3

# mybatis-plus配置 
#mybatis-plus.mapper-#locations=classpath:/mapper/**/*Mapper.xml
#mybatis-plus.type-aliases-package=com.police.model.entity
#mybatis-plus.configuration.log-#impl=org.apache.ibatis.logging.stdout.StdOutImpl
#mybatis-plus.global-config.db-config.id-type=auto
#mybatis-plus.global-config.db-config.logic-delete-value=1
#mybatis-plus.global-config.db-config.logic-not-delete-value=0

spring boot 启动类上加上响应的信息

在spring boot 启动类上添加扫描mapper注解 - @MapperScan(“com.police.biz.dao.*”) 

DataSourceAutoConfiguration.class默认会帮我们自动配置单数据源,所以,如果想在项目中使用多数据源就需要排除它,手动指定多数据源 -@SpringBootApplication(scanBasePackages = "com.police", exclude = {DataSourceAutoConfiguration.class})

个人的项目结构

 

 

在springboot项目地址中创建config包(注意框架结构)并加入...

/**
 * 多数据源枚举
 */
public enum DBTypeEnum {

    db1("db1"),
    db2("db2");

    private String value;
    DBTypeEnum(String value) {
        this.value = value;
    }
    public String getValue() {
        return value;
    }
}
/**
 * 创建数据源操作类DbContextHolder
 */
public class DbContextHolder {

    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();
    }
}
/**
 * 扩展Spring的AbstractRoutingDataSource抽象类,实现动态数据源。
 * AbstractRoutingDataSource中的抽象方法determineCurrentLookupKey是实现数据源的route的核心,
 * 这里对该方法进行Override。 【上下文DbContextHolder为一线程安全的ThreadLocal】
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    /**
     * 取得当前使用哪个数据源
     * @return
     */
    @Override
    protected Object determineCurrentLookupKey(){
        return DbContextHolder.getDbType();
    }
}
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.JdbcType;
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 javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class MybatisPlusConfig {

    /**
     * 分页拦截器
     * @return
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }

    /**
     * 定义db1 与配置文件中的层次结构相同
     * @return
     */
    @Bean(name = "db1")
    @ConfigurationProperties(prefix = "spring.datasource.db1")
    public DataSource db1() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 定义db2 与配置文件中的层次结构相同
     * @return
     */
    @Bean(name = "db2")
    @ConfigurationProperties(prefix = "spring.datasource.db2")
    public DataSource db2() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 动态数据源配置
     * @return
     */
    @Bean
    @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 {
        //***导入MybatisSqlSession配置***
        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
        //指明数据源
        sqlSessionFactory.setDataSource(multipleDataSource(db1(), db2()));
        //指明mapper.xml位置(配置文件中指明的xml位置会失效用此方式代替,具体原因未知)
        sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:/mapper/**Mapper.xml"));
        //指明实体扫描(多个package用逗号或者分号分隔)
        sqlSessionFactory.setTypeAliasesPackage("com.police.model.entity");

        //***导入Mybatis配置***
        MybatisConfiguration configuration = new MybatisConfiguration();
        configuration.setJdbcTypeForNull(JdbcType.NULL);
        configuration.setMapUnderscoreToCamelCase(true);
        configuration.setCacheEnabled(false);
        sqlSessionFactory.setConfiguration(configuration);
        sqlSessionFactory.setPlugins(new Interceptor[]{paginationInterceptor()});

        //***导入全局配置***
//        sqlSessionFactory.setGlobalConfig(globalConfiguration());
        return sqlSessionFactory.getObject();
    }

    /**
     * 在代码中配置MybatisPlus替换掉application.yml中的配置
     * @return
     */
//    @Bean
//    public GlobalConfiguration globalConfiguration() {
//        GlobalConfiguration conf = new GlobalConfiguration(new LogicSqlInjector());
//        //主键类型 0:数据库ID自增, 1:用户输入ID,2:全局唯一ID (数字类型唯一ID), 3:全局唯一ID UUID
//        conf.setIdType(0);
//        //字段策略(拼接sql时用于判断属性值是否拼接) 0:忽略判断,1:非NULL判断,2:非空判断
//        conf.setFieldStrategy(2);
//        //驼峰下划线转换含查询column及返回column(column下划线命名create_time,返回java实体是驼峰命名createTime,开启后自动转换否则保留原样)
//        conf.setDbColumnUnderline(true);
//        //是否动态刷新mapper
//        conf.setRefresh(true);
//        return conf;
//    }
}

当更新操作时可以会需要给一些字段填充一些统一的数据

/**
 * 填充策略
 */
@Component
public class MyBatisPlusMeta implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName("createTime", new Date(), metaObject);
        this.setFieldValByName("cjsj", new Date(), metaObject);
        this.setFieldValByName("cjjlsj", new Date(), metaObject);
        this.setFieldValByName("updateTime", new Date(), metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updateTime", new Date(), metaObject);
    }
}

模仿dynamic@DS切换数据源的注解

@Documented
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DS {

    DBTypeEnum dataSource() default DBTypeEnum.db1;
}
@Aspect
@Component
@Slf4j
public class DSAspect {

    @Pointcut("execution(* com.police..*.*(..))")
    public void executionController() {
    }

    @Before("executionController()&&@annotation(DS)")
    public void methodBefore(JoinPoint joinPoint) {
        log.info("切换数据源");
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        DS ds = signature.getMethod().getAnnotation(DS.class);
        DBTypeEnum dataSource = ds.dataSource();
        DbContextHolder.setDbType(dataSource);
    }
}

测试一波

@RestController
@RequestMapping("/test")
public class TestController {

    @Autowired
    private SysUserMapper2 userMapper2;

    @Autowired
    private SysUserMapper1 userMapper1;

    
    @GetMapping("/a")
    public JsonResult a() {
        userMapper.insert(new SysUser());
        return new JsonResult(userMapper1.selectList(null));
    }

    @DS(dataSource = DBTypeEnum.db2)
    @GetMapping("/b")
    public JsonResult b() {
        return new JsonResult(userMapper2.selectList(null));
    }

   
}

由于个人项目不方便展示,这里找了CSDN的博客地址https://blog.csdn.net/qq_37502106/article/details/91044952后面有源码参考

posted @ 2022-01-08 10:11  隐琳琥  阅读(803)  评论(0编辑  收藏  举报