SpringBoot+MyBatis利用切面的方式配置多数据源

3 篇文章 0 订阅
1 篇文章 0 订阅

SpringBoot+MyBatis利用切面的方式配置多数据源

SpringBoot+MyBatis利用切面的方式配置多数据源,同时用druid监控sql

重点

1、AOP相关的知识,这里主要是切点的定位
2、重写AbstractRoutingDataSource的determineCurrentLookupKey方法
3、数据源的切换

项目结构

在分包的基础上增加切面相关的配置
![在这里插入图片描述](https://img-blog.csdnimg.cn/8dd45e82294749f1abaa528042ea5ab7.png
在这里插入图片描述

项目依赖

 		<!-- mybatis -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.0</version>
        </dependency>

        <!--mysql依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.16</version>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
           <groupId>com.alibaba</groupId>
           <artifactId>druid-spring-boot-starter</artifactId>
           <version>1.1.10</version>
       </dependency>
       
       <dependency>
           <groupId>com.alibaba</groupId>
           <artifactId>druid</artifactId>
           <version>1.1.10</version>
       </dependency>

配置文件

spring.datasource.db1.url=jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.db1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.db1.username=root
spring.datasource.db1.password=123456
spring.datasource.db1.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.db1.filters=stat
spring.datasource.db1.testOnBorrow=false
spring.datasource.db1.testOnReturn=false
spring.datasource.db1.testWhileIdle=true
spring.datasource.db1.validationQuery=SELECT 1
spring.datasource.druid.db1.inital-size=3
spring.datasource.druid.db1.min-idle=3
spring.datasource.druid.db1.max-active=10
spring.datasource.druid.db1.max-wait=60000
spring.datasource.druid.db1.filters.slf4j.enabled=true

spring.datasource.db2.url=jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.db2.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.db2.username=root
spring.datasource.db2.password=123456
spring.datasource.db2.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.db2.filters=stat
spring.datasource.db2.testOnBorrow=false
spring.datasource.db2.testOnReturn=false
spring.datasource.db2.testWhileIdle=true
spring.datasource.db2.validationQuery=SELECT 1 FROM DUAL
spring.datasource.druid.db2.inital-size=3
spring.datasource.druid.db2.min-idle=3
spring.datasource.druid.db2.max-active=10
spring.datasource.druid.db2.max-wait=60000
spring.datasource.druid.db2.filters.slf4j.enabled=true

mybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

数据源配置

1、MultipleDatasourceConfig

/**
 * 多数据源配置
 */
@Configuration
public class MultipleDatasourceConfig {
    @Bean(name = "db1")
    @ConfigurationProperties(prefix = "spring.datasource.db1")
    public DataSource db1() {
        return DataSourceBuilder.create().type(DruidDataSource.class).build();
    }

    @Bean(name = "db2")
    @ConfigurationProperties(prefix = "spring.datasource.db2")
    public DataSource db2(){
        return DataSourceBuilder.create().type(DruidDataSource.class).build();
    }

    @Primary
    @Bean("dynamicDatasource")
    public DataSource dynamicDatasource() {
        DynamicDatasource dynamicDatasource = new DynamicDatasource();
        // 配置默认数据源
        dynamicDatasource.setDefaultTargetDataSource(db1());

        // 配置多数据源
        Map<Object, Object> datasourceMap = new HashMap<>();
        datasourceMap.put("db2", db2());
        dynamicDatasource.setTargetDataSources(datasourceMap);

        return dynamicDatasource;
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dynamicDatasource());
    }
}

2、db1

/**
 * @author code
 * @version 1.0
 * @Date 2022/6/15 17:48
 * @Description ${mysql数据源配置}
 */
@Configuration
@MapperScan(basePackages = "路径",sqlSessionFactoryRef = "mysqlSqlSessionFactory")
public class DB1DSConfig {
     static final String MAPPER_LOCATION = "classpath:mapper/mysql/*.xml";

     @Resource
     DataSource mysqlDataSource;

     @Bean(name = "mysqlSqlSessionFactory")
     @Primary
     public SqlSessionFactory mysqlSqlSessionFactory(@Qualifier("mysqlDataSource") DataSource mysqlDataSource) throws Exception {
          SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
          sqlSessionFactoryBean.setDataSource(mysqlDataSource);
          //将下滑线映射为驼峰配置
          sqlSessionFactoryBean.setConfiguration(configuration());
          sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MysqlDSConfig.MAPPER_LOCATION));
          return sqlSessionFactoryBean.getObject();
     }

     @Bean(name = "mysqlTransactionManager")
     @Primary
     public DataSourceTransactionManager mysqlTransactionManager(){
          return new DataSourceTransactionManager(mysqlDataSource);
     }

     @Bean(name = "mysqlTransactionTemplate")
     @Primary
     public SqlSessionTemplate mysqlTransactionTemplate(@Qualifier("mysqlSqlSessionFactory")SqlSessionFactory sqlSessionFactory){
          return new SqlSessionTemplate(sqlSessionFactory);
     }

     @Bean
     @Primary
     @ConfigurationProperties(prefix = "mybatis.configuration")
     public org.apache.ibatis.session.Configuration configuration(){
          return new org.apache.ibatis.session.Configuration();
     }
}

3、db2

/**
 * @author code
 * @version 1.0
 * @Date 2022/6/15 17:48
 * @Description ${mysql数据源配置}
 */
@Configuration
@MapperScan(basePackages = "路径",sqlSessionFactoryRef = "mysqlSqlSessionFactory")
public class DB2DSConfig {
     static final String MAPPER_LOCATION = "classpath:mapper/mysql/*.xml";

     @Resource
     DataSource mysqlDataSource;

     @Bean(name = "mysqlSqlSessionFactory")
     @Primary
     public SqlSessionFactory mysqlSqlSessionFactory(@Qualifier("mysqlDataSource") DataSource mysqlDataSource) throws Exception {
          SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
          sqlSessionFactoryBean.setDataSource(mysqlDataSource);
          //将下滑线映射为驼峰配置
          sqlSessionFactoryBean.setConfiguration(configuration());
          sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MysqlDSConfig.MAPPER_LOCATION));
          return sqlSessionFactoryBean.getObject();
     }

     @Bean(name = "mysqlTransactionManager")
     @Primary
     public DataSourceTransactionManager mysqlTransactionManager(){
          return new DataSourceTransactionManager(mysqlDataSource);
     }

     @Bean(name = "mysqlTransactionTemplate")
     @Primary
     public SqlSessionTemplate mysqlTransactionTemplate(@Qualifier("mysqlSqlSessionFactory")SqlSessionFactory sqlSessionFactory){
          return new SqlSessionTemplate(sqlSessionFactory);
     }

     @Bean
     @Primary
     @ConfigurationProperties(prefix = "mybatis.configuration")
     public org.apache.ibatis.session.Configuration configuration(){
          return new org.apache.ibatis.session.Configuration();
     }
}

动态的数据源对象

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Datasource {
    String value() default "db1";
}

DataSourceType容器

/**
 * 动态数据源持有类
 */
public class DatasourceContextHolder {
    private static final Logger logger = LoggerFactory.getLogger(DatasourceContextHolder.class);

    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    public static void setDatasource(String name) {
        logger.info("切换到[{}]数据源", name);
        contextHolder.set(name);
        
    }

    public static String getDatasource() {
        return contextHolder.get();
    }

    public static void clearDatasource() {
        contextHolder.remove();
    }
}

切换数据源

AbstractRoutingDataSource类是Spring为方便的在项目中实现动态切换数据源而提供的一个抽象类, 我们只需要实现上面的这个抽象方法 即可 , 该方法用于返回当前需要切换的数据源的key;

/**
 * 动态数据源
 */
public class DynamicDatasource extends AbstractRoutingDataSource {
    private static final Logger logger = LoggerFactory.getLogger(DynamicDatasource.class);

//    public DynamicDatasource(Datasource datasource, Map<Object,Object> objectMap){
//        this.setDefaultTargetDataSource(datasource);
//        this.setTargetDataSources(objectMap);
//        this.afterPropertiesSet();
//    }

    @Override
    protected Object determineCurrentLookupKey() {
        String datasource = DatasourceContextHolder.getDatasource();
        if (datasource == null) {
            logger.info("当前数据源为[db1]");
            datasource = "db1";
        } else {
            logger.info("当前数据源为[{}]", datasource);
        }

        return datasource;
    }
}

切面拦截切换数据源

@Component
@Aspect
@Order(-1)// 使该切面调用优先于AbstractRoutingDataSource的determineCurrentLookupKey()
public class DynamicDatasourceAspect {
    @Pointcut("@annotation(数据源对象的路径,即Datasource的路径)")
    public void pointCut() {
        System.out.println("使用[@Pointcut]定义切入点");
    }

    @Before("pointCut()")
    public void before(JoinPoint joinPoint) {
        try {
            // 获取当前访问的class
            Class<?> aClass = joinPoint.getTarget().getClass();

            // 获取当前访问的method
            String methodName = joinPoint.getSignature().getName();

            // 获取方法的参数类型数组
            Class[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();

            // 获取方法对象
            Method method = aClass.getDeclaredMethod(methodName, parameterTypes);

            Datasource datasource = method.getAnnotation(Datasource.class);
            if (null!=datasource){
                DatasourceContextHolder.setDatasource(datasource.value());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            DatasourceContextHolder.clearDatasource();
        }
    }

    @After("pointCut()")
    public void after() {
        System.out.println("after");

        DatasourceContextHolder.clearDatasource();
    }
}

切点的路径即,上文动态的数据源对象的路径,也可以定义在service层,但是我这边只有定义在数据源对象这里才能实现切换

dao层配置指定数据源

1、db1

@Datasource(value = "db1")

2、db2

@Datasource(value = "db2")
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值