本文共 5227 字,大约阅读时间需要 17 分钟。
上篇文章讲述了一种简单粗暴的多数据源配置方式,这篇来讲一下动态切换数据源的方式配置。
动态切换数据源,其核心在于一个AbstractRoutingDataSource类,通过继承此类并重写determineCurrentLookupKey方法可以实现动态切换数据源,具体切换方式可点进去看源码的determineTargetDataSource方法,比较简单,这里只记录实现。
数据库准备
参考上篇文章:http://www.scarlettbai.com/index.php/archives/112.html动态数据源配置
public class MyDynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DynamicDataSourceContextHolder.get(); }}
这个方法的返回值,就是一个数据源的key,我们对于多个数据源,会对每个数据源定义一个key,之后以map形式保存在AbstractRoutingDataSource的targetDataSources属性中。
接下来看一下上文中出现的DynamicDataSourceContextHolder类:
public class DynamicDataSourceContextHolder { private static ThreadLocalDBNAME = new ThreadLocal<>(); public static String get() { return DBNAME.get(); } public static void set(String dbName) { DBNAME.set(dbName); } public static void clear() { DBNAME.remove(); }}
DynamicDataSourceContextHolder类的作用很简单,就是根据线程来存取数据库的key。
DataSource配置
@Configurationpublic class DataSourceConfig { @Autowired private Environment env; @Bean public DataSource backDataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl(env.getProperty("spring.datasource.back.jdbcUrl")); dataSource.setUsername(env.getProperty("spring.datasource.back.username")); dataSource.setPassword(env.getProperty("spring.datasource.back.password")); return dataSource; } @Bean public DataSource frontDataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl(env.getProperty("spring.datasource.front.jdbcUrl")); dataSource.setUsername(env.getProperty("spring.datasource.front.username")); dataSource.setPassword(env.getProperty("spring.datasource.front.password")); return dataSource; } @Bean @Primary public DataSource dynamicDataSource() { MyDynamicDataSource dataSource = new MyDynamicDataSource(); dataSource.setDefaultTargetDataSource(frontDataSource()); Map
这里datasource配置引入了阿里的druid数据源,各位不用的话可以切换为自己的数据源即可,主要注意一下dynamicDataSource这个数据源,这里需要将他设置为主数据源,即@Primary。之后调用数据源都会走determineTargetDataSource进行数据源路由切换。
在调用dao前进行数据源切换
这里通过AOP方式实现,看各人系统需求,可以直接通过aop扫描对应的分包目录,也可以自定义一个注解来自己在方法上添加注解。
//定义注解@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface DynamicDataSource { /** * dbname * @return */ String value();}//AOP实现@Aspect@Componentpublic class DynamicDataSourceAOP { @Before("@annotation(org.white.mutidatasource2.aop.DynamicDataSource)") public void changeDataSource(JoinPoint point) { Class clazz = point.getTarget().getClass(); MethodSignature signature = (MethodSignature) point.getSignature(); try { Method method = clazz.getDeclaredMethod(signature.getName(), signature.getParameterTypes()); if (method.isAnnotationPresent(DynamicDataSource.class)) { DynamicDataSource dynamicDataSource = method.getAnnotation(DynamicDataSource.class); String dbName = dynamicDataSource.value(); if (DynamicDataSourceContextAware.names.contains(dbName)) { DynamicDataSourceContextHolder.set(dbName); } } } catch (Exception e) { System.out.println("error" + e.getMessage()); } } @After("@annotation(org.white.mutidatasource2.aop.DynamicDataSource)") public void after() { DynamicDataSourceContextHolder.clear(); }}
这里的aop实现也很简单,就是通过读取注解内容来切换DynamicDataSourceContextHolder中的值。
其中的DynamicDataSourceContextAware类代码如下,只是记录一个names的集合,可以看需求省略:
@Componentpublic class DynamicDataSourceContextAware implements ApplicationContextAware { public static Setnames = new HashSet<>(); @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { Map dataSources = applicationContext.getBeansOfType(DataSource.class); for (String dataSourceName : dataSources.keySet()) { if (!"dynamicDataSource".equals(dataSourceName)) { names.add(dataSourceName); } } }}
最后,来看一下调用方式:
@Servicepublic class BizServiceImpl implements BizService { @Autowired private AdminMapper adminMapper; @Autowired private UserMapper userMapper; //无注解,使用默认datasource @Override public int addUser(UserDTO userDTO) { return userMapper.insert(userDTO); } //使用backDatasource @Override @DynamicDataSource(value = "backDataSource") public int addAdmin(AdminDTO adminDTO) { return adminMapper.insert(adminDTO); }}
实现过程就在这里了,其核心其实就是一个AbstractRoutingDataSource类,其他都是一些spring的基础知识。
这种模式优点在于扩展方便,新增数据源只需新增一个datasource的配置即可。
另外这里附上github项目地址:https://github.com/whiteBX/mutidatasource2
可以直接下载运行,包括初始化sql都在项目内。
转载地址:http://xhsni.baihongyu.com/