提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
多数据源切换和分表插件
前言
在我们工作中会遇到需要同步数据到多个库(数据库)中,这个时候我们需要定义多个数据源,但是我们一个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) 这样就可以了
总结
根据需求进行不同数据库的数据插入