背景:
项目原先只有一个数据库(开发工作已基本完成),后来又添加了一个库,两个库数据结构一致,查询逻辑基本一致,只是数据对应的年份不一样,客户提出的需求是根据可以自主选择查询不同年份的数据,而默认框架实现里没有多数据源的方法。
项目基础框架:
SpringBoot+Mybatis
解决方案:
有两套系统同时使用这两个库,另外一个系统的同学的解决方案是另开一个服务器,直接使用不同的数据库配置。
我觉得这样干不符合我的审美,考虑通过前台在用户选择年份后,固定传递年份参数,后台根据此参数动态切换数据源的方式实现。
实现方式如下:
1、定义一个注解TimeCare,对需要动态切换的controller或method进行标注(即带有此注解的controller或者方法在执行时需要根据年份动态切换数据源,其他的使用默认数据源)。
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TimeCare { }
2、定义切面,对有TimeCare注解的执行过程进行拦截。
@Around(value="@within(co.lewis.aspect.TimeCare)")
public Object arround(ProceedingJoinPoint pjp) throws Throwable {
HttpServletRequest request =((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
String year=request.getParameter("year");
if(year==null||year.trim().length()==0){
year= ConstParameters.default_last_year;
}
DataSourceWrapper.resetDataSource(year);//设置年份,此处使用一个ThreadLocal对象存储年份数据
Object ret=pjp.proceed();
DataSourceWrapper.resetDataSource(null);//将年份信息重新置空
return ret;
}
3、定义一个动态代理类,代替默认的DataSource实现类
@Configuration
@EnableConfigurationProperties({DataSource2015.class.......})//注册数据源1、数据源2......
public class DataSourceWrapper implements InvocationHandler {
public static void resetDataSource(String key){
year.set(key);
}
private static ThreadLocal<String> year=new ThreadLocal<>();
@Bean(name = "dataSource",destroyMethod = "close") //注册datasource bean
public DataSource getDataSource(){
return (DataSource) this.bind();//生成代理对象
}
public Object bind(){
return Proxy.newProxyInstance(getClass().getClassLoader(),
new Class[]{DataSource.class,AutoCloseable.class, CommonDataSource.class, Wrapper.class},this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(detemine(),args);//在调用数据源内部方法时动态决定使用哪一个数据源
}
DataSource detemine(){}
}
4、在application.properties文件中添加数据源配置
dataSource2015.url=jdbc:oracle:thin:@111.111.111.111:1521/ORCl
dataSource2015.username=username1
dataSource2015.password=passwd1
dataSource2015.driver-class-name=oracle.jdbc.driver.OracleDriver
dataSource2016.url=jdbc:oracle:thin:@111.111.111.112:1521/ORCl
dataSource2016.username=username2
dataSource2016.password=passwd2
dataSource2016.driver-class-name=oracle.jdbc.driver.OracleDriver
5、实现数据源注册类DataSource2015、DataSource2016等(略)
如此,便实现了在不做太大改动的情况下使用多个数据源。即使以后又添加新的数据库,也可以通过简单添加新的DataSource注册类的方式实现新数据库查询。