sprinboot mybatis使用继承AbstractRoutingDataSource获取多数据源

sprinboot mybatis使用继承AbstractRoutingDataSource获取多数据源

AbstractRoutingDataSource:这个类是实现多数据源的关键,他的作用就是动态切换数据源,实质:有多少个数据源就存多少个数据源在targetDataSources(是AbstractRoutingDataSource的一个map类型的属性,其中value为每个数据源,key表示每个数据源的名字)这个属性中,然后根据determineCurrentLookupKey()这个方法获取当前数据源在map中的key值,然后determineTargetDataSource()方法中动态获取当前数据源,如果当前数据源不存并且默认数据源也不存在就抛出异常。

一、application.properties配置文件

# 配置多数据源
spring.datasource.db1.url=jdbc:mysql://127.0.0.1:3306/kou
spring.datasource.db1.username=root
spring.datasource.db1.password=admin
# 使用druid数据源
spring.datasource.db1.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.db1.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.db1.max-idle=10
spring.datasource.db1.max-wait=10000
spring.datasource.db1.min-idle=5
spring.datasource.db1.initial-size=5

# 第二个数据库
spring.datasource.db2.url=jdbc:mysql://127.0.0.1:3306/zheng
spring.datasource.db2.username=root
spring.datasource.db2.password=admin
spring.datasource.db2.driver-class-name=com.mysql.jdbc.Driver

# mapper映射文件
mybatis.type-aliases-package=com.test.spike.*
mybatis.mapper-locations=classpath*:com/test/spike/**/*.xml
#开启驼峰命名转换
mybatis.configuration.map-underscore-to-camel-case=true

二、定义数据库

      枚举出多数据源

package com.test.spike.datasources.aop;

/**
 * 列出所有数据源key(常用数据库名称来命名)
 * 注意:
 * 1)这里数据源与数据库是一对一的
 * 2)DatabaseType中的变量名称就是数据库的名称
 */
public enum DataSourceType {
    kou, zheng
}

三、使用线程保存当前所需要的数据源

package com.test.spike.datasources.aop;

/**
 * 构建一个DataSourceType容器,并提供了向其中设置和获取DataSourceType的方法
 * 保存一个线程安全的DatabaseType容器
 */
public class DataBaseContextHolder {

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

    /**
     * 向当前线程里添加数据源类型
     * @param type
     */
    public static void setDataSourceType(DataSourceType type) {
        if (type == null) {
            throw new NullPointerException();
        }
        contextHolder.set(type);
    }

    /**
     * 获取数据源类型
     * @return
     */
    public static DataSourceType getDataSourceType() {

        DataSourceType type = contextHolder.get();
        if (type == null) {
            // 如果当前数据源类型为空,取默认数据源
            type = DataSourceType.kou;
        }
        System.err.println("[获取当前数据源的类型为]:" + type);
        return type;
    }

    /**
     * 清空数据源类型
     */
    public static void clearDataSourceType() {
        contextHolder.remove();
    }

}

四、定义一个动态数据源:继承AbstractRoutingDataSource 抽象类,并重写determineCurrentLookupKey()方法

package com.test.spike.datasources.aop;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * 动态数据源(需要继承AbstractRoutingDataSource)
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {

        return DataBaseContextHolder.getDataSourceType();
    }
}

五、定义多数据源

package com.test.spike.datasources.aop;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * springboot集成mybatis的基本入口
 * 1)创建数据源(如果采用的是默认的tomcat-jdbc数据源,则不需要)
 * 2)创建SqlSessionFactory 3)配置事务管理器,除非需要使用事务,否则不用配置
 * @MapperScan(basePackages) basePackages 要写只有dao层接口的路径,不然其他bean也会被变成ibatis的bean了,直接会报
 * org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.test.spike.biz.SeckillService.getSeckillListPH
 */
@Configuration
@MapperScan(basePackages = {"com.test.spike.dao", "com.test.spike.db2"}, sqlSessionFactoryRef = "sqlSessionFactory")
public class DataSourceConfig {

    @Autowired
    private Environment env;

    /**
     * 设置默认数据库
     * @return
     */
    @Primary
    @Bean(name="kouDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.db1")
    public DataSource getDataSource() {
        return DataSourceBuilder.create().build();
    }

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

    /**
     * 创建数据源
     * @Primary 该注解表示在同一个接口有多个实现类可以注入的时候,默认选择哪一个,而不是让@autowire注解报错
     * @param kouDataSource
     * @param zhengDataSource
     * @return
     */
    @Bean
    public DynamicDataSource dynamicDataSource(@Qualifier("kouDataSource") DataSource kouDataSource,
                                        @Qualifier("zhengDataSource") DataSource zhengDataSource) {

        Map<Object, Object> targetDataSource = new HashMap<>();
        targetDataSource.put(DataSourceType.kou, kouDataSource);
        targetDataSource.put(DataSourceType.zheng, zhengDataSource);

        DynamicDataSource dataSource = new DynamicDataSource();
        dataSource.setTargetDataSources(targetDataSource);
        // 设置默认数据库
        dataSource.setDefaultTargetDataSource(kouDataSource);

        return dataSource;
    }

    /**
     * 根据数据源创建SqlSessionFactory
     * @return
     */
    @Bean
    public SqlSessionFactory sqlSessionFactory(DynamicDataSource dynamicDataSource) throws Exception {

        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        // 指定数据源
        bean.setDataSource(dynamicDataSource);
        // 下边两句仅仅用于*.xml文件,如果整个持久层操作不需要使用到xml文件的话(只用注解就可以搞定),则不加
//        bean.setTypeAliasesPackage(env.getProperty("mybatis.typeAliasesPackage"));
//        bean.setTypeAliasesPackage(env.getProperty("type-aliases-package"));
        String mybatisMapperLocations = "";
        if (null != env.getProperty("mybatis.mapperLocations")) {
            mybatisMapperLocations = env.getProperty("mybatis.mapperLocations");
        } else if (null != env.getProperty("mybatis.mapper-locations")) {
            mybatisMapperLocations = env.getProperty("mybatis.mapper-locations");
        } else {
            mybatisMapperLocations = "classpath*:com/test/spike/**/*.xml";
        }
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mybatisMapperLocations));

        return bean.getObject();
    }

    /**
     * 配置事务管理器
     * @return
     */
    @Bean
    public DataSourceTransactionManager transactionManager(DynamicDataSource dynamicDataSource) {

        return new DataSourceTransactionManager(dynamicDataSource);
    }

}

注意:

1、@Primary 要写在数据源上,不能写在

      public DynamicDataSource dynamicDataSource 方法上,写在这里会报 
      The dependencies of some of the beans in the application context form a cycle:

2、@MapperScan(basePackages = {"com.test.spike.dao", "com.test.spike.db2"} 要写dao层接口文件目录,不然其他bean也会被变成ibatis的bean了,直接会报

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.test.spike.biz.SeckillService.getSeckillListPH

SeckillService 是service接口bean,getSeckillListPH这个是接口的方法,报这个错原因看过了,被代理成ibatis了

六、调用sql时动态添加sql所在的数据源,可以使用aop切入也可以使用在调用之前加 DataBaseContextHolder.setDataSourceType(DataSourceType.kou); 设置数据库

 

aop切入dao层接口

package com.test.spike.datasources.aop;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

/**
 * 多数据源切面
 */
@Aspect
@Component
public class DataSourceAop {

    @Before("execution(* com.test.spike.dao..*.*(..))")
    public void setKouDataSource() {
        System.out.println("kou数据库");
        DataBaseContextHolder.setDataSourceType(DataSourceType.kou);
    }

    @Before("execution(* com.test.spike.db2..*.*(..))")
    public void setZhengDataSource() {
        System.out.println("zheng数据库");
        DataBaseContextHolder.setDataSourceType(DataSourceType.zheng);
    }
}

 

七、文件目录截图

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值