项目中如何使用java代码配置多数据源,并能指定某些查询使用某个数据源

1、首先需要思考,配置多数据源需要实现什么?是不是要根据不同的查询来指定是查主库还是查从库
2、那在学过的知识中,什么方法可以实现在执行某个查询前根据指定的数据库查询?Aspect(切面)

实现思路:
1、多数据源场景,怎么指定使用哪个数据源呢?
需要有个注解,通过注解来指定

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
   String name() default "";
}

2、定义切面处理类

@Aspect
@Component
public class DataSourceAspect implements Ordered {
   protected Logger logger = LoggerFactory.getLogger(getClass());
  
   @Point("@annotation(多数据源注解接口所在完整类名-包名.类名)")
   public void dataSourcePointCut(){
       //nothing
   }
  
    @Around("dataSourcePointCut()")
    public Object  around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature  signature = (MethodSignature)point.getSignature();
        Method method = signature.getMethod();
        
        DataSource ds = method.getAnnotation(DataSource.class);
        if(ds == null){
           //未指定数据源时,默认的数据源
           DynamicDataSource.setDataSource("dataSource1");
        }else{
           //指定数据源时,使用指定的数据源
           DynamicDataSource.setDataSource(ds.name());
        }

       try{
          return point.proceed();
        }finally{
           DynamicDataSource.clearDataSource();
        }
  }
}

3、创建一个继承 AbstractRoutingDataSource 的类,来实现数据源的动态切换逻辑

public class DynamicDataSource extends AbstractRoutingDataSource {
    // 当前使用的数据源标识
    public static ThreadLocal<String> CURRENT_HOLDER = new ThreadLocal<>();

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

    public static void setDataSource(String dataSource) {
       CURRENT_HOLDER.set(dataSource);
    }

     public static void getDataSource() {
       CURRENT_HOLDER.get();
     }

    public static void clearDataSource() {
       CURRENT_HOLDER.remove();
     }
    
    public DynamicDataSource(DataSource defaultTargetDataSource,Map<Object,Object> targetDataSources) {
    
        super.setTargetDataSources(targetDataSources);
        // 为defaultTargetDataSource 设置默认的数据源
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.afterPropertiesSet();
    }

}

4、配置多数据源

@Configuration
@MapperScan(basePackages = "mapper包名",sqlSessionTemplateRef = "mySqlSessionTemplate")
public class DataSourceConfig {
	/**
		数据库1的配置
	/
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.datasource1") //数据库1配置名前缀
    public DataSource dataSource1() {
        // 底层会自动拿到spring.datasource中的配置, 创建一个DataSource
        return DataSourceBuilder.create().build();
    }

	/**
		数据库2的配置
	/
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.datasource2")  //数据库1配置名前缀
    public DataSource dataSource2() {
        // 底层会自动拿到spring.datasource中的配置, 创建一个DataSource
        return DataSourceBuilder.create().build();
    }

   @Bean(name="mysqlDataSource")
   @DependsOn({"dataSource1","dataSource2"})
    @Primary
    public DynamicDataSource  dataSource (DataSource dataSource1,DataSource dataSource2){
        Map<Object,Object> targetDataSources = new HashMap<>();
        targetDataSources.put("dataSource1",dataSource1);
        targetDataSources.put("dataSource2",dataSource2);
        return  new DynamicDataSource(dataSource1,targetDataSources);
    }

   @Bean(name="mysqlSessionFactory")
    @Primary
    public SessionFactory   mysqlSessionFactory(@Qualifier("mysqlDataSource") DataSource dataSource){
        SqlSessionFactoryBean  bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        //定义分页拦截器,通过拦截 SQL 语句,动态修改查询逻辑,添加分页参数(如 LIMIT 和 OFFSET),从而简化分页代码的编写
        PageInterceptor   pageInterceptor  = new PageInterceptor();
        Properties   properties  = new Properties();
        properties.setProperty("helperDialect","mysql");
        properties.setProperty("offsetAsPageNum","true");
        properties.setProperty("rowBoundsWithCount","true");
        properties.setProperty("reasonable","true");
        properties.setProperty("supportMethodsArguments","true");
        properties.setProperty("params","pageNum=pageNum;pageSize=pageSize;");
        pageInterceptor.setProperties(properties);
        bean.setPlugins(pageInterceptor);
        //classpath:mysql xml的路径
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mysql/*.xml"));  
        return bean.getObject();
    }
    
    @Bean(name="mysqlTransactionManager")
    @Primary
    public DataSourceTransactionManager mysqlTransactionManager(@Qualifier("mysqlDataSource") DynamicDataSource dataSource){
        return  new DataSourceTransactionManager(dataSource);
    }

     @Bean(name="mySqlSessionTemplate")
    @Primary
    public SqlSessionTemplate  mySqlSessionTemplate(@Qualifier("mysqlSessionFactory") SqlSessionFactory  sqlSessionFactory){
        return  new SqlSessionTemplate(sqlSessionFactory);
    }
}

5、使用方法
在dao层或者impl接口上,通过@DataSource(name =“dataSource1”) 来指定查询哪个数据源

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐