java mysql 多数据源,运行时动态切换,兼容MybatisPlus

1.数据源

#其它配置
spring:
  #MySQL数据库
  datasource:
    main:
      jdbc-url: jdbc:mysql:///zcfx_main?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=GMT%2B8
      username: root
      password: trkj.2020   
    slave1:
      jdbc-url: jdbc:mysql:///zcfx_a?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=GMT%2B8
      username: root
      password: trkj.2020
    slave2:
      jdbc-url: jdbc:mysql:///zcfx_b?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=GMT%2B8
      username: root
      password: trkj.2020

2.创建数据源枚举,方便运行时切换

public enum DatabaseType {
	DATASOURCE_A,DATASOURCE_B,DATASOURCE_MAIN
}

3.设置数据源切换类

public class DatabaseContextHolder {
	 private static final ThreadLocal<DatabaseType> contextHolder = new ThreadLocal<>();
	    public static void setDatabaseType(DatabaseType type) {
	        contextHolder.set(type);
	    }
	    public static DatabaseType getDatabaseType() {
	        return contextHolder.get();
	    }
}

4.动态数据源继承AbstractRoutingDataSource类

public class DynamicDataSource extends AbstractRoutingDataSource {

	@Override
    protected Object determineCurrentLookupKey() {
        return DatabaseContextHolder.getDatabaseType();
    }
}

5.数据源配置

/** 定义数据源配置-主库 */
@Configuration
@MapperScan(basePackages="bw.zcfx.svc.module", sqlSessionFactoryRef="mainSqlSessionFactory")
public class DataSourceMainConfig
{
	@Primary													//表示这个数据源是默认数据源
	@Bean("mainDataSource")										//将这个对象放入Spring容器中
	@ConfigurationProperties(prefix="spring.datasource.main")	//读取配置文件中的参数,并指定参数名的前缀
	public DataSource getDataSource()
	{
		return DataSourceBuilder.create().build();
	}
    @Bean("datasource1")
    @ConfigurationProperties(prefix = "spring.datasource.slave1")
    public DataSource dataSource1() {
    	return DataSourceBuilder.create().build();
    }
    @Bean("datasource2")
    @ConfigurationProperties(prefix = "spring.datasource.slave2")
    public DataSource dataSource2() {
    	return DataSourceBuilder.create().build();
    }
	
    //配置多数据源,使用ThreadLocal对对应的线程保存不用的类型,根据类型获取不同的数据源
    @Bean("datasource")
    @Primary
    public DynamicDataSource dataSource(@Qualifier("datasource1") DataSource dataSource1, 
@Qualifier("datasource2") DataSource dataSource2,@Qualifier("mainDataSource") DataSource mainDataSource) {
        Map<Object, Object> targetDataSource = new HashMap<>();
        targetDataSource.put(DatabaseType.DATASOURCE_A, dataSource1);
        targetDataSource.put(DatabaseType.DATASOURCE_B, dataSource2);
        targetDataSource.put(DatabaseType.DATASOURCE_MAIN, mainDataSource);

        DynamicDataSource dataSource = new DynamicDataSource();
        dataSource.setTargetDataSources(targetDataSource);
        dataSource.setDefaultTargetDataSource(mainDataSource);
        return dataSource;
    }
	
	@Primary
	@Bean("mainSqlSessionFactory")
	public SqlSessionFactory mainSqlSessionFactory(@Qualifier("datasource") DataSource datasource) throws Exception
	{
		 MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
	        sqlSessionFactory.setDataSource(datasource);
	        MybatisConfiguration configuration = new MybatisConfiguration();
	        configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);
	        configuration.setJdbcTypeForNull(JdbcType.NULL);
	        sqlSessionFactory.setConfiguration(configuration);
	        sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
	                .getResources("classpath:mapper/**/*.xml"));
	        sqlSessionFactory.setPlugins(new Interceptor[]{
	                new PaginationInterceptor(),
	                new PerformanceInterceptor(),
	                new OptimisticLockerInterceptor()
	        });
	        return sqlSessionFactory.getObject();
	}
	
	@Primary
	@Bean("mainSqlSessionTemplate")
	public SqlSessionTemplate mainSqlSessionTemplate(@Qualifier("mainSqlSessionFactory") SqlSessionFactory sessionfactory)
	{
		return new SqlSessionTemplate(sessionfactory);
	}
	
	@Bean("mainTransactionManager")
	@Primary
	public PlatformTransactionManager mainTransactionManager(@Qualifier("mainDataSource")DataSource dataSource) {
		return new DataSourceTransactionManager(dataSource);
	}

6.调用持久层时,选择数据库
DatabaseContextHolder.setDatabaseType(DatabaseType.DATASOURCE_A);
我是写了个工具类,获取配置自动设置

public class MysqlUtil {
	public static void selectMysql() {
		String mYSQL_NUM = AppBuffer.MYSQL_NUM;
		if("A".equals(mYSQL_NUM)) {
			DatabaseContextHolder.setDatabaseType(DatabaseType.DATASOURCE_A);
		}
		if("B".equals(mYSQL_NUM)) {
			DatabaseContextHolder.setDatabaseType(DatabaseType.DATASOURCE_B);
		}
	}
	public static void setMysql(DatabaseType databaseType) {
		DatabaseContextHolder.setDatabaseType(databaseType);
	}
}

7.1 测试方法1
注:动态设置只对当前调用线程起作用,如果同一个线程想再次调用其他数据库,建议每次调用设置数据源,返回时切换回默认数据源

/** 查询所有的配置(包括不可见的) */
	public List<SysConfigPO> getAll()
	{
		MysqlUtil.selectMysql();
		List<SysConfigPO> selectList = baseMapper.selectList(null);
		MysqlUtil.setMysql(DatabaseType.DATASOURCE_MAIN);
		return selectList;
	}
@GetMapping("/getSlave")
	@ApiOperation(value="查询从库", notes="")
	public CommonResponse getSlave()
	{
		CommonResponse resp = new CommonResponse<SysUserVO>();
		
		List<SysConfigPO> list = sysConfigService.getAll();
		resp.addData("data1",list);
		List<SysConfigPO> list2 = sysConfigService.lambdaQuery().list();
		resp.addData("data2",list2);
		return resp;
	}

在这里插入图片描述
7.2 测试方法2

	/** 查询所有的配置(包括不可见的) */
	public List<SysConfigPO> getAll()
	{
		MysqlUtil.selectMysql();
		List<SysConfigPO> selectList = baseMapper.selectList(null);
//		MysqlUtil.setMysql(DatabaseType.DATASOURCE_MAIN);
		return selectList;
	}

在这里插入图片描述
看两次测试结果,第一次调用没切换回默认库,线程向下进行第二次调用还是使用从库
8.为了减小代码侵入可以使用AOP切入service方法
8.1.新增切入注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {

	String value() default "";
}

8.2切入程序

/**
 * 切换数据源,切面处理类
 *
 */
@Aspect
@Component
public class DataSourceAspect {
	@Pointcut("@annotation(bw.zcfx.svc.common.annotation.DataSource)")
	public void pointCut() {

	}

	@Around("pointCut()")
	public Object around(ProceedingJoinPoint point) throws Throwable {
		MysqlUtil.selectMysql();
		// 执行方法
		Object result = point.proceed();
		MysqlUtil.setMysql(DatabaseType.DATASOURCE_MAIN);
		return result;
	}
}

8.3测试方法

	@DataSource
	public List<SysConfigPO> getAll()
	{
		return baseMapper.selectList(null);
	}

在这里插入图片描述
9.数据库表
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值