概念
数据源:指的是一个具体的数据库连接。例如 localhost:3306 或者 localhost:3306/sysdb
具体的数据库:当前打开的数据源里的数据库对象的集合,一般称之为database或者schema
这里的SAAS系统考虑数据库的扩展性,使用了多数据源
考虑到一个数据源可以容纳多个租户的数据库,因此1个数据源是要容纳30到100个租户数据库的。
具体是参考 https://github.com/helloworlde/SpringBoot-DynamicDataSource/tree/aspect_dao
上边这个git工程可以解决动态数据源的创建,动态切换的问题。
动态创建数据源的方法
private DataSource getDataSource(String name) {
DataSourceBuilder builder = DataSourceBuilder.create().driverClassName("com.mysql.jdbc.Driver");
if ("master".equals(name)) {
builder.url("jdbc:mysql://localhost:3306/product_master?useSSL=false");
} else {
builder.url("jdbc:mysql://localhost:3306/product_slave?useSSL=false");
}
builder.username("root");
builder.password("root");
return builder.build();
}
动态切换数据源依赖于DynamicRoutingDataSource
@Bean("dynamicDataSource")
public DataSource dynamicDataSource() {
DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
Map
以及在ThreadLocal里保存当前需要的数据源名字,然后在合适的切面(service )方法执行前进行切换即可
切换的方法
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
private final Logger logger = LoggerFactory.getLogger(getClass());
/**
* Set dynamic DataSource to Application Context 设置当前应用上下文所使用的数据源
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
logger.info("Current DataSource is [{}]", DynamicDataSourceContextHolder.getDataSourceKey());
return DynamicDataSourceContextHolder.getDataSourceKey();
}
}
还需要一个动态打开具体的数据库(schema)的方法。在dao方法里的sql实际执行前进行拦截
mybatis拦截器
@Intercepts(@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}))
public class MyBatisInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
Connection o = (Connection)invocation.getArgs()[0];
o.createStatement().executeQuery("use sys_0001");//schema 隔离 threalocal sys_0001
System.out.println(o.getClass());
Object result = invocation.proceed();
System.out.println("Invocation.proceed()");
return result;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// TODO Auto-generated method stub
}
}