spring boot mybatis 优雅的数据源切换

当一个项目有多个数据源时,我们可以通过spring 切面优雅的实现数据源切换

1.定义切面

public DataSourceAspect(){
        logger.debug("选择数据源---");
    }
    private static final Logger logger = LoggerFactory.getLogger(DataSourceAspect.class);
    @Pointcut("@within(com.some.web.db.DataSource) || @annotation(com.some.web.db.DataSource)")
    public void pointCut(){
        logger.debug("选择数据源---");
    }

    @Before("pointCut() && @annotation(dataSource)")
    public void doBefore(DataSource dataSource){
        logger.debug("选择数据源---"+dataSource.value().getValue());
        DataSourceContextHolder.setDataSource(dataSource.value().getValue());
    }

    @After("pointCut()&& @annotation(dataSource)")
    public void doAfter(DataSource dataSource){
        logger.debug("清除数据源---"+dataSource.value().getValue());
        DataSourceContextHolder.remove();
    }

 

2.切面自动把切换的数据源入栈

public class DataSourceContextHolder {
    private static final Logger logger = LoggerFactory.getLogger(DataSourceContextHolder.class);
    private static final ThreadLocal<Stack<String>> contextHolder = new InheritableThreadLocal<>();

    /**
     *  设置数据源
     * @param db
     */
    public static void setDataSource(String db){
        Stack<String> st = contextHolder.get();
        if(st==null){
            st = new Stack<String>();
            contextHolder.set(st);
        }
        st.push(db);
    }

    /**
     * 取得当前数据源
     * @return
     */
    public static String getDataSource(){
        Stack<String> st = contextHolder.get();
        //不移除
        return st==null||st.isEmpty()?null:st.peek();
    }

    /**
     * 清除上下文数据
     */
    public static void clear(){
        contextHolder.remove();
    }

    /**
     * 清除当前db
     */
    public static void remove(){
        Stack<String> st = contextHolder.get();
        if(st==null||st.isEmpty()){
            logger.warn("数据源切换异常");
        }else {
            st.pop();
        }
    }

}

3. 定义多数据源路由的数据源 这个设置为Primary

public class MultipleDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSource();
    }

}

4.注册数据源

@Configuration
public class DataSourceConfig {

	@Bean(name = "someDataSource1")
	@Qualifier("someDataSource1")
	@ConfigurationProperties(prefix = "spring.datasource.some1")
	public DataSource primaryDataSource() {
		return DataSourceBuilder.create().type(com.alibaba.druid.pool.DruidDataSource.class).build();
	}

	@Bean(name = "someDataSource2")
	@Qualifier("someDataSource2")
	@ConfigurationProperties(prefix = "spring.datasource.some2")
	public DataSource primaryDataSource() {
		return DataSourceBuilder.create().type(com.alibaba.druid.pool.DruidDataSource.class).build();
	}

	

	@Bean(name = "multipleDataSource")
	@Qualifier("multipleDataSource")
	@Primary
	public DataSource multipleDataSource(@Qualifier("someDataSource1") DataSource some1, @Qualifier("someDataSource2") DataSource some2) {
		MultipleDataSource multipleDataSource = new MultipleDataSource();
		Map< Object, Object > targetDataSources = new HashMap<>();
		targetDataSources.put(DataSourceEnum.SOME1.getValue(), some1);
		targetDataSources.put(DataSourceEnum.SOME2.getValue(), some2);
		//添加数据源
		multipleDataSource.setTargetDataSources(targetDataSources);
		//设置默认数据源
		multipleDataSource.setDefaultTargetDataSource(some1);
		return multipleDataSource;
	}
}

 

5.通过在方法加注解切换

  @DataSource(DataSourceEnum.SOME1)
 public void getMaxVip(){
   }
public enum  DataSourceEnum {
	 SOME1("some1"),SOME2("some2")

	    private String value;

	    DataSourceEnum(String value){this.value=value;}

	    public String getValue() {
	        return value;
	    }
}

spring 配置

spring:
  datasource:
    some1:
        type: com.alibaba.druid.pool.DruidDataSource
        driverClassName: com.mysql.jdbc.Driver
        url: 
        username: 
        password: 
        initialize: false
        sqlScriptEncoding: UTF-8
        initialSize: 5
        minIdle: 5
        maxActive: 10
        maxWait: 60000
        timeBetweenEvictionRunsMillis: 600000
        minEvictableIdleTimeMillis: 3000000
        validationQuery: SELECT 1 FROM DUAL
        testWhileIdle: true  #在检查闲置连接时同时检查连接可用性
        testOnBorrow: false
        testOnReturn: false
        poolPreparedStatements: true
        maxPoolPreparedStatementPerConnectionSize: 20
    some2:
        type: com.alibaba.druid.pool.DruidDataSource
        driverClassName: com.mysql.jdbc.Driver
        url: 
        username: 
        password: 
        initialize: false
        sqlScriptEncoding: UTF-8
        initialSize: 5
        minIdle: 5
        maxActive: 10
        maxWait: 60000
        timeBetweenEvictionRunsMillis: 600000
        minEvictableIdleTimeMillis: 3000000
        validationQuery: SELECT 1 FROM DUAL
        testWhileIdle: true  #在检查闲置连接时同时检查连接可用性
        testOnBorrow: false
        testOnReturn: false
        poolPreparedStatements: true
        maxPoolPreparedStatementPerConnectionSize: 20

备注:spring boot 默认开启open-in-view,所以在controller层同时调用两个带事务service时(service注解了不同数据源),切换会失效,因为事务在controller方法只会获取一次数据源连接,第二次不会获取,一般建议如果不需要懒加载功能则把open-in-view关闭,配置

spring:
  jpa:
    open-in-view: false
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值