多数据源切换和分表插件

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

在我们工作中会遇到需要同步数据到多个库(数据库)中,这个时候我们需要定义多个数据源,但是我们一个sqlSessionFactory只能使用一个数据源,下面我就实现一下多个数据源的切换


一、AbstractRoutingDataSource使用

该类有个getConnection的方法,这个方法就是连接数据库的操作,而且从这个方法中看出需要实现determineCurrentLookupKey方法来找到想连接的数据源,那我们就从这个方法入手就行了

Public class DynamicDataSource extends AbstractRoutingDataSource implements Serializable
	@Override
	protected Object determineCurrentLookupKey(){
		//使用ThreadLocal存储当前线程使用的数据源,一个线程对应一个数据源
		String datasource = DataSourceContextHolder.getDB();
		//如果这里返回null,就会使用我们设置的默认数据源,在下面代码中
		return datasource;
	}

二、DataSourceContextHolder

//使用ThreadLocal存储当前线程使用的数据源,一个线程对应一个数据源
public class DataSourceContextHolder{
	private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
	public static void setDB(DBEnum dbEnum){
		contextHolder.set(dbEnum.getValue());
	}
	public static String getDB(DBEnum dbEnum){
		contextHolder.get();
	}
	
	public static void clearDB(){
		contextHolder.remove();
	}
}

public enum DBEnum{
	ABC("abc"),
	QWE("qwe");
	private final String value;
	public String getValue(){
		return value;
	}
}

三、MybatisPlusConfig

@Configuration
@EnableTransactionManagement
@MapperScan()
@Slf4j
public class MybatisPlusConfig{
	@Bean(name="ABC")
	@ConfigurationProperties(prefix = "spring.datasource.druid.ABC")
	public DataSource ABCDemo(){
	return DruidDataSourceBuilder.create().build();
	}
	@Bean(name="QWE")
	@ConfigurationProperties(prefix = "spring.datasource.druid.QWE")
	public DataSource QWEDemo(){
	return DruidDataSourceBuilder.create().build();
	}
   @Bean("multipleDataSource")
   @Primary
   public DataSource multipleDataSource(@Qualifier("ABC") DataSource abcDemo,
   										@Qualifier("QWE") DataSource qweDemo){
   	DynamicDataSource dynamicDataSource = new DynamicDataSource();
   	Map<Object,Object> targetDataSources =new HashMap<>();
   	targetDataSources.put(DBEnum.ABC.getValue(),abcDemo);
   	targetDataSources.put(DBEnum.QWE.getValue(),qweDemo);
   	dynamicDataSource.setTargetDataSources(targetDataSources);
   	//程序默认数据源,根据程序调用数据源频次,把常调用的数据源作为默认
   	dynamicDataSource.setDefaultTargetDataSource(abcDemo);
   	return dynamicDataSource;}

	@Bean("datasourceSqlSessionFactory")
	@ConfigurationPropertiesBinding()
	public SqlSessionFactory SqlSessionFactory() throws Exception{
		MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
		sqlSessionFactory.setDataSource(multipleDataSource(ABCDemo(),QWEDemo()));
		//设置默认需要扫描的xml文件
		sqlSessionFactory.setMapperLocations(new PathMatchingResourcePathternResolver().getResources("classpath*:mapper/**/*.xml"));
		//其他配置项
		MybatisConfiguration configuration = new MybatisConfiguration();
		configuration.setJdbcTypeForNull(JdbcType.NULL);
		//开启驼峰和下划线转换
		configuration.setMapUnderscoreToCamelCase(true);
		//开启二级缓存的
		configuration.setCacheEnaled(false);
		//防止使用Map时,value为空过滤掉这个数据
		configuration.setCallSetterOnNulls(true);
		//添加插件
		sqlSessionFactory.setPlugins(mybatisPlusInterceptor())
		sqlSessionFactory.setConfiguration(configuration);
		//数据库查询结果驼峰式返回
		sqlSessionFactory.setObjectWrapperFactory(new MybatisMapWrapperFactory());
		//添加用于批量插入的SQL注入器
		sqlSessionFactory.setGlobalConfig(globalConfig());
		return sqlSessionFactory.getObject();
	}
	//动态事务配置
	@Bean(name="multipleTransactionMapper")
	@Primary
	public DataSourceTransactionMapper multipleTransactionMapper(@Qualifier("multipleDataSource") DataSource dataSource){
	return new DataSourceTransactionMapper(dataSource);
	}
//有需求就看这个方法,自定义分表插件,分页插件
	@Bean
	public MybatisPlusInterceptor mybatisPlusInterceptor(){
		MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
		DynamicTablerNameInnerInterceptor dynamicTablerNameInnerInterceptor = new DynamicTablerNameInnerInterceptor();
		dynamicTablerNameInnerInterceptor.setTableNameHandler(new EnterpriseTableNameHandler(
		//这里填写需要分表的名称,比如用户表
		"User"
		));
		//添加分表插件
		interceptor.addInnerInterceptor(dynamicTablerNameInnerInterceptor);
		//添加分页插件
		interceptor.addInnerInterceptor(new PaginationInnerInterceptor);
		return interceptor;
	}
	@Bean
	public GlobalConfig globalConfig(){
		GlobalConfig globalConfig = new GlobalConfig();
		globalConfig.setSqlInjector(easySqlInjector());
		return globalConfig;
	}
	
	@Bean
	public EasySqlInjector easySqlInjector (){
		return new easySqlInjector();
	}
	@Bean(name="ABCJdbcTemplate")
	public JdbcTemplate ABCJdbcTemplate(@Qualifier("ABC") DataSource dataSource)			   {
	return new JdbcTemplate(dataSource)
	}
		@Bean(name="QWEJdbcTemplate")
	public JdbcTemplate QWEJdbcTemplate(@Qualifier("QWE") DataSource dataSource)			   {
	return new JdbcTemplate(dataSource)
	}
	
}

四、EasySqlInjector类

public class EasySqlInjector  extends DefaultSqlInjector{

	@Override
	public List<AbstractMethod> getMethodList(Class<?> mapperClass,TableInfo tableInfo){
	//调用了DefaultSqlInjector的getMethodList方法,保留了mybatis—plus的自带方法
	List<AbstractMethod> methodList = super.getMethodList(mapperClass,tableInfo);
		methodList.add(new InsertBatchSomeColumn());
		return methodList;
	}
}

五、切面应用

代码如下(示例):

//数据源注解
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSourceAnno{
	DBEnum value default DBEnum.ABC;
}
@Component
@Order(value=-100)
@Slf4j
@Aspect
public class DataSourceAspect{
	@Before("execution(* 包路径  mapper..*.(..) || execution(* 包路径 service..*.*(..)) || @annotation(DataSourceAnno注解路径)")
	public void before(JoinPoint joinPoint){
	MethodSignature signature = (MethodSignature) joinPoint.getSignature();
	Method method= signature.getMethod();
	DataSourceAnno dataSourceAnno = null;
	if(method.isAnnotationPresent(dataSourceAnno.class)){
		dataSourceAnno = method.getAnnotation(DataSourceAnno.class);
		DataSourceContextHolder.setDB(dataSourceAnno.getValue());
	}else if(method.getDeclaringClass().isAnnotationPresent(dataSourceAnno.class)){
	dataSourceAnno= method.getDeclaringClass().getAnnotation(DataSourceAnno.class);
	DataSourceContextHolder.setDB(dataSourceAnno.getValue());
	}else{
	//查看切点的Service类上是否有主注解。
		Type[] types = AopUtil.getTargetClass(joinPoint.getTarget).getGenericInterfaces();
		if(types.length>0){
			Class<?> targetClass = (Class<?>)types[0];
			if(targetClass.isAnnotationPresent(dataSourceAnno.class)){
				dataSourceAnno = targetClass.getAnnotation(DataSourceAnno.class);
		DataSourceContextHolder.setDB(dataSourceAnno.getValue());
			}
		}
	}
	}
}

五、使用

直接在Mapper接口 或者 Service接口中使用
@DataSourceAnno(DBEnum.ABC) 这样就可以了

总结

根据需求进行不同数据库的数据插入

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值