梦想还是要有的,万一忘了咋办?

0%

Spring动态数据源

目录:

  • 原理
  • 实现动态数据源
  • 实战
  • 问题

原理

spring动态数据源
看似复杂的东西都有一个极其简单的内核。在java中sql执行过程可以拆解为:

  • 拿到 Datasource 对象;
  • 打开链接 ds.getConnections();
  • 执行sql

Spring实现动态数据源


如图所示:

  • DynamicDataSource实现DataSource,注入spring容器中供使用;
    • 这里面的逻辑 spring 做了支持,可以直接继承 AbstractRoutingDataSource 类即可;
  • DataSourceManager
    • 通过配置文件加载多个数据源
    • 在线程中维护一个 Queue;
    • 接口 use(datasourceId)接口,将新数据源标识(datasourceId)放入队列;
    • 接口 over(),删除队列顶部数据;
    • 接口 getDataSource(),返回队列顶部datasourceId 对应的数据源;
  • DataSource注解,可以设置datasourceId值;
  • DataSourceAspect ,切面;
    • 对所有添加DataSource注解的方法设置切入点;
    • 切入点执行前,更具DataSource的datasourceId 设置要使用的数据源;
    • 切入点执行
    • 切入点执行后,重置数据源(没有设置时不需要重置数据源);

实战

这里可以参考renren-security框架里的动态数据源实现,请移步:Renren框架Gitee地址

存在的问题

多数问题时因为无法进入切面、进而无法动设置数据源;事务是由于一旦开启事务、connection会被缓存在事务管理器里面,再执行sql时不会再通过Datasource获取链接,因此导致 无法切换数据源;

事务不能跨数据源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//数据源不会切换
aservice.m1();
//数据源会切换
aservice.m3();


class Aservice{
@Transactional
void m1(){
m2();
}
@DataSource("slave1)
void m2(){}

@Transactional
@DataSource("slave1)
void m3(){}

}

子类 不能向父类传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//数据源不会切换
aservice.abc();
//有效、想要父类方法支持动态数据源必须在子类重写父类方法
cservice.abc();

class AService{
void abc(){...}
}
@Service
@DataSource("slave1")
class BService extends AService{
}

@Service
@DataSource("slave1")
class CService extends AService{
void abc(){super.abc();}
}

接口不能向实现类传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//数据源不会切换
XService service;
service.inInterface();


@DataSource("slave1")
interface XService {
@DataSource("slave1")
void inInterface();
}
class AService implements XService {
@Override
public void inInterface() {
sysUserDao.getByUsername("nihao");
}
}