spring aop 动态切换数据源

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

 

 


前言

spring aop 动态切换数据源,从源数据库导入数据到目标数据库

1,需要实现AbstractRoutingDataSource接口重写determineCurrentLookupKey()方法

2,实现ApplicationRunner接口重写run()方法,程序启动的时候就可以从数据表中(也可以从配置文件中读取)初始化Target,Resource数据源

 


提示:以下是本篇文章正文内容,下面案例可供参考

一、AbstractRoutingDataSource是什么?

AbstractRoutingDataSource的getConnection() 方法根据查找 lookup key 键对不同目标数据源的调用,通常是通过(但不一定)某些线程绑定的事物上下文来实现。

AbstractRoutingDataSource的多数据源动态切换的核心逻辑是:在程序运行时,把数据源数据源通过 AbstractRoutingDataSource 动态织入到程序中,灵活的进行数据源切换。

基于AbstractRoutingDataSource的多数据源动态切换,可以实现读写分离

二、使用步骤

1.实现逻辑

    1,定义DynamicDataSource类继承抽象类AbstractRoutingDataSource,并实现了determineCurrentLookupKey()方法。

     2,把配置的多个数据源会放在AbstractRoutingDataSource的 targetDataSources和defaultTargetDataSource中,然后通过afterPropertiesSet()方法将数据源分别进行复制到resolvedDataSources和resolvedDefaultDataSource中。

     3,调用AbstractRoutingDataSource的getConnection()的方法的时候,先调用determineTargetDataSource()方法返回DataSource在进行getConnection()。

2.初始数据库连接信息表

	public void dataSourceInit() {
		logger.info("cityPartner==========开始加载数据源");
		DynamicDataSource dynamicDataSource =(DynamicDataSource) DynamicDataSourceContextHolder.applicationContext.getBean("dataSource");
	    Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
		Environment env =DynamicDataSourceRegister.environment;
        //从数据库表中读取数据源(目标和源文件数据源)
		List<TenantCitypartnerDatasource> tcds=DatasourceMapper.selectDataSource();
		if(tcds!=null&&tcds.size()>0){
			for(TenantCitypartnerDatasource tcd:tcds){
				Map<String, Object> dsMap = new HashMap<>();
				dsMap.put("driver-class-name","com.mysql.jdbc.Driver");
                dsMap.put("url",tcd.getUrl());
                dsMap.put("username", tcd.getUsername());
                dsMap.put("password", tcd.getPassword());
                DataSource ds = CityPartnerDataSourceRegister.buildDataSource(dsMap);//创建DataSourceBuilder
                CityPartnerDataSourceRegister.dataBinder(ds, env);//绑定主数据源
                CityPartnerDataSourceRegister.targetDataSources.put(tcd.getDataSourceType(), ds);
                CityPartnerDataSourceRegister. DBdataSourceInfo.put(tcd.getDataSourceType(), tcd);
                CityPartnerDataSourceRegister.realConnectionInfo.put(tcd.getDataSourceType(), getDataSourceKey(tcd));
   				logger.info("加载:{}数据源数据源,{}库,成功",tcd.getSystemCode(),tcd.getService_name());
			}
			 // 添加更多数据源
			targetDataSources.putAll(CityPartnerDataSourceRegister.targetDataSources);
		}
		dynamicDataSource.setTargetDataSources(targetDataSources);
        dynamicDataSource.afterPropertiesSet();//这一行非常关键
        //DynamicDataSourceContextHolder.dataSourceIds.clear();
        DynamicDataSourceContextHolder.dataSourceIds.addAll(CityPartnerDataSourceRegister.targetDataSources.keySet());
	}

//生成key
public String getDataSourceKey(TenantCitypartnerDatasource tcd){
		return tcd.getSystemCode()+"-"+tcd.getService_name();
	}

 

 

 

 

保存初始化的数据源信息类

public class CityPartnerDataSourceRegister  {
    private static final Logger logger = LoggerFactory.getLogger(CityPartnerDataSourceRegister.class);
    
    // 如配置文件中未指定数据源类型,使用该默认值
    private static final Object DATASOURCE_TYPE_DEFAULT = "com.alibaba.druid.pool.DruidDataSource";
    
    private static ConversionService conversionService = new DefaultConversionService(); 
    
    private static PropertyValues dataSourcePropertyValues;
    
    public static Map<String, DataSource> targetDataSources = new HashMap<String, DataSource>();
    
    public static Map<String, String> realConnectionInfo = new  HashMap<String, String>();
    
    //保存数据库连接信息
    public static Map<String, TenantCitypartnerDatasource> DBdataSourceInfo = new HashMap<String, TenantCitypartnerDatasource>();

    @SuppressWarnings("unchecked")
    public static DataSource buildDataSource(Map<String, Object> dsMap) {
        try {
            Class<? extends DataSource> dataSourceType;
            dataSourceType = (Class<? extends DataSource>) Class.forName((String) DATASOURCE_TYPE_DEFAULT);
            String driverClassName = dsMap.get("driver-class-name").toString();
            String url = dsMap.get("url").toString();
            String username = dsMap.get("username").toString();
            String password = dsMap.get("password").toString();
            DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url)
            		.username(username).password(password).type(dataSourceType);
           return  factory.build();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
    /**
     * 为DataSource绑定更多数据
     *
     * @param dataSource
     * @param env
     */
    public static void dataBinder(DataSource dataSource, Environment env){
        RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);
        //dataBinder.setValidator(new LocalValidatorFactory().run(this.applicationContext));
        dataBinder.setConversionService(conversionService);
        dataBinder.setIgnoreNestedProperties(false);//false
        dataBinder.setIgnoreInvalidFields(false);//false
        dataBinder.setIgnoreUnknownFields(true);//true
        if(dataSourcePropertyValues == null){
            Map<String, Object> rpr = new RelaxedPropertyResolver(env, "spring.datasource").getSubProperties(".");
            Map<String, Object> values = new HashMap<>(rpr);
            // 排除已经设置的属性
            values.remove("type");
            values.remove("driver-class-name");
            values.remove("url");
            values.remove("username");
            values.remove("password");
            dataSourcePropertyValues = new MutablePropertyValues(values);
        }
        dataBinder.bind(dataSourcePropertyValues);
    }
}
public class DynamicDataSourceContextHolder {
	public static ApplicationContext applicationContext;
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
    public static List<String> dataSourceIds = new ArrayList<>();

    public static void setDataSourceType(String dataSourceType) {
        contextHolder.set(dataSourceType);
    }

    public static String getDataSourceType() {
        return contextHolder.get();
    }

    /**
     * 判断指定DataSrouce当前是否存在
     *
     */
    public static boolean containsDataSource(String dataSourceId){
        return dataSourceIds.contains(dataSourceId);
    }
}

 spring AOP

DataSourceEM  方法枚举

//自定义注解
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
	DataSourceEM source();
}




public enum DataSourceEM {

	// sourceName必须和数据库里的dataSourceType一致
	SOURCE_RESOURCE("source_resource","saas-resource库"), 
	SOURCE_USER("source_user","saas-user库"), 
	SOURCE_DEVICE("source_device","saas-device库"), 
	TARGET_SAASMAIN("target_saas_main","saas_main库"),
	TARGET_DEVICE("target_device","device库"),
	TARGET_USER("target_user","user库"),
	TARGET_RESOURCE("target_resource","resource库");
	
	private String sourceName;
	private String desc;
	
	private DataSourceEM(String sourceName,String desc){
		this.sourceName = sourceName;
		this.desc = desc;
	}

	public String getSourceName() {
		return sourceName;
	}

	public void setSourceName(String sourceName) {
		this.sourceName = sourceName;
	}

// todo get set...

}

 拦截处理代码

@Aspect
@Component  
@Order(1)
public class DataSourceInteceptor {
	  private static final Logger logger = LoggerFactory.getLogger(DataSourceInteceptor.class);
	  long i = 0;
  	
	  @Autowired
	  private Environment env;
	  
	  
	  @Pointcut("@annotation(com.zdst.tenant.manage.web.cityPartnerDataSource.TargetDataSource)")  
	  public void dataSourcePointcut(){}  
	    
	    
	    @Before("dataSourcePointcut()")
	    public void before(JoinPoint pjp) throws Exception {
	    	
	    	MethodSignature signature=(MethodSignature) pjp.getSignature();
	        Method method=signature.getMethod();
	        TargetDataSource targetDataSource=method.getAnnotation(TargetDataSource.class);
	        Object[] args = pjp.getArgs();
	        String dataName=targetDataSource.source().getSourceName();
	        if(dataName!=null&&dataName!=""){
	 	       if(DynamicDataSourceContextHolder.containsDataSource(dataName)){
	 	    	  logger.info("cityPartner===aop切换数据源:{},{}",dataName,CityPartnerDataSourceRegister.realConnectionInfo.get(dataName));
	 	    	 DynamicDataSourceContextHolder.setDataSourceType(dataName);
	 	       }else {
	 	    	  logger.info("cityPartner===aop切换数据源失败,没有找到{}这个数据源,切回默认数据源:",dataName);
	 	    	 DynamicDataSourceContextHolder.setDataSourceType(null);
	 	       }
	        }else {
	        	logger.info("cityPartner===aop切换数据源dataName值为空,切回默认数据源");
	        	DynamicDataSourceContextHolder.setDataSourceType(null);
	        }
	    }
	    
	    /** 
	     * 拦截器具体实现 
	     * @param pjp 
	     * @return JsonResult(被拦截方法的执行结果,或需要登录的错误提示。) 
	     * @throws Throwable 
	     */  
	    @Around("dataSourcePointcut()") 
	    public Object Interceptor(ProceedingJoinPoint pjp) throws Throwable{
	    	return pjp.proceed();  
	    }
	    @After("dataSourcePointcut()")
	    public void after() {
	    	logger.info("cityPartner===切回默认数据库");
	    	DynamicDataSourceContextHolder.setDataSourceType(null);
	    }
	    
	    

	  
}

 

 


总结

DataSourceEM 枚举里的sourceName必须和数据库里的dataSourceType一致

只是处理datax导入的数据在做处理,大表数据处理还是用datax导入数据

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值