利用Aop实现动态分库分表
通过继承spring-jdbc提供的AbstractRoutingDataSource抽象类来实现选取DataSource。使用方法就是在bean加载的时候给AbstractRoutingDataSource内部的targetDatasourceMap注入多个数据源。
jdbcTemplate的bean定义如下所示,这样就可以在用到jdbcTemplate的地方进行动态的切换。
@Bean
@Scope("prototype")
public JdbcTemplate jdbcTemplate(DatasourceDynamicRouter datasourceRouter){
return new JdbcTemplate(datasourceRouter);
}
具体切换到哪个数据源是根据determineCurrentLookupKey这个方法的返回值来决定的。其中DatasourceHolder是根据自己的业务逻辑实现的一个工具类。
public class DatasourceDynamicRouter extends AbstractRoutingDataSource {
private Logger log = LoggerFactory.getLogger(this.getClass());
下面附上DatasourceHolder的一个demo
public final class DatasourceHolder {
private final static Logger log = LoggerFactory.getLogger(DatasourceHolder.class);
private final static ThreadLocal<String> datasourceKey = new ThreadLocal<>();
private static JdbcTemplate jdbcTemplate;
public static void setDatasourceKey(String key, DataSourceKey dataSourceKey) throws Exception {
if(dataSourceKey == DataSourceKey.SHARD_KEY){
//do something
datasourceKey.set(key);
}else if(dataSourceKey == DataSourceKey.PRODUCT_NAME){
//do something
datasourceKey.set(shardKey);
}
}
public static String getDatasourceKey(){
return datasourceKey.get();
}
public static void clearDatasourceKey(){
datasourceKey.remove();
}
}
将标注有DatasourceShardKey的参数作为key值,来选取DataSource
我们在产品名PRODUCT_NAME上标注上DatasourceShardKey,也就是根据产品名来选择数据库。
解决相互调用的问题:
分库代码使用spring AOP开发, 跟其他Spring AOP技术实现的功能比如@Async, @Transactional 一样, 当在同一个类中函数相互调用的时候,被调用的函数不会被AOP拦截增强。我们列举一个可能出现问题的场景如下:
public Class xxxDao{
public void bizDao1(
如上面逻辑代码所示: xxxDao.bizDao1("Product_1", ...), 但是 在同一个类中 调用了 bizDao2, 查询的产品确实 Product_2的。这个时候 bizDao2 因为不会被 Spring AOP拦截增强,所以 其实 bizDao2还是查询的 Product_1库的东西。
解决方案很多我们列举两种
第一种:使用 exposeProxy 的方法, AopContext.currentProxy()
public Class xxxDao{
public void bizDao1(
第二种: 自己注入自己
public Class xxxDao{