是从springmvc的思路上来做的,主要就是配置主、从DataSource,
再继承AbstractRoutingDataSource,重写determineCurrentLookupKey
方法,通过Context结合 aop 进行数据主、从库的切换。

上代码:

路由,即实现多数据库的切换源

/*
* 重写的函数决定了最后选择的DataSource
* 因为AbstractRoutingDataSource中获取连接方法为:
@Override
public Connection getConnection(String username, String password) throws SQLException {
return determineTargetDataSource().getConnection(username, password);
}
*/ public class MultiRouteDataSource extends AbstractRoutingDataSource { @Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSource();
} }

注解,即用以标识选择主还是从数据库

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
String value();
}

常规配置项,具体主从继承并通过

@ConfigurationProperties(prefix = "master.datasource") 进行配置读取

public class BaseDataSourceConfig {

    private String url;
private String username;
private String password;
private String driverClassName;
// 添加上getter、setter方法
}

多数据源设置

@Configuration
public class DataSourceComponent { @Resource
MasterDataSourceConfig masterDataSourceConfig; @Resource
FirstDataSourceConfig firstDataSourceConfig; @Resource
SecondDataSourceConfig secondDataSourceConfig; /*
* 一开始以为springboot的自动配置还是会生效,直接加了@Resource DataSource dataSource;
* 显示是不work的,会报create bean 错误
*/
public DataSource masterDataSource() {
DataSource dataSource = new DataSource();
dataSource.setUrl(masterDataSourceConfig.getUrl());
dataSource.setUsername(masterDataSourceConfig.getUsername());
dataSource.setPassword(masterDataSourceConfig.getPassword());
dataSource.setDriverClassName(masterDataSourceConfig.getDriverClassName());
return dataSource;
} /*
* 一开始在这里加了@Bean的注解,当然secondDataSource()也加了
* 会导致springboot识别的时候,发现有多个
* 所以,其实都不要加@Bean,最终有效的的DataSource就只需要一个multiDataSource即可
*/
public DataSource firstDataSource() {
DataSource dataSource = new DataSource();
dataSource.setUrl(firstDataSourceConfig.getUrl());
dataSource.setUsername(firstDataSourceConfig.getUsername());
dataSource.setPassword(firstDataSourceConfig.getPassword());
dataSource.setDriverClassName(firstDataSourceConfig.getDriverClassName());
return dataSource;
} public DataSource secondDataSource() {
DataSource dataSource = new DataSource();
dataSource.setUrl(secondDataSourceConfig.getUrl());
dataSource.setUsername(secondDataSourceConfig.getUsername());
dataSource.setPassword(secondDataSourceConfig.getPassword());
dataSource.setDriverClassName(secondDataSourceConfig.getDriverClassName());
return dataSource;
} @Bean(name = "multiDataSource")
public MultiRouteDataSource exampleRouteDataSource() {
MultiRouteDataSource multiDataSource = new MultiRouteDataSource();
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("master", masterDataSource());
targetDataSources.put("first", firstDataSource());
targetDataSources.put("second", secondDataSource());
multiDataSource.setTargetDataSources(targetDataSources);
multiDataSource.setDefaultTargetDataSource(masterDataSource());
return multiDataSource;
} @Bean(name = "transactionManager")
public DataSourceTransactionManager dataSourceTransactionManager() {
DataSourceTransactionManager manager = new DataSourceTransactionManager();
manager.setDataSource(exampleRouteDataSource());
return manager;
} @Bean(name = "sqlSessionFactory")
public SqlSessionFactoryBean sqlSessionFactory() {
SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
sessionFactoryBean.setDataSource(exampleRouteDataSource());
return sessionFactoryBean;
}
}

当然少不了DataSourceContextHolder,用以保持当前线程的数据源选择。

public class DataSourceContextHolder {

    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    public static void setDataSource(String value) {
contextHolder.set(value);
} public static String getDataSource() {
return contextHolder.get();
} public static void clearDataSource() {
contextHolder.remove();
}
}

最后,自然就是AOP+注解实现数据源切换啦

@Aspect
@Component
public class DynamicDataSourceAspect { @Around("execution(public * com.wdm.example.service..*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
Method targetMethod = methodSignature.getMethod();
if(targetMethod.isAnnotationPresent(TargetDataSource.class)){
String targetDataSource = targetMethod.getAnnotation(TargetDataSource.class).value() ;
DataSourceContextHolder.setDataSource(targetDataSource);
}
Object result = pjp.proceed();
DataSourceContextHolder.clearDataSource();
return result;
}
}

那用法就是如下了:

package com.wdm.example.service;

import java.util.Date;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;

import com.wdm.example.dao.UserDao;
import com.wdm.example.datasource.TargetDataSource;
import com.wdm.example.model.User;
import com.wdm.example.service.UserService; /*
* @author wdmyong
* 20170416
*/
@Service
public class UserService { @Resource
UserDao userDao; public User getById(Integer id) {
return userDao.getById(id);
} @TargetDataSource("master")
public User getById0(Integer id) {
return userDao.getById(id);
} @TargetDataSource("first")
public User getById1(Integer id) {
return userDao.getById(id);
} @TargetDataSource("second")
public User getById2(Integer id) {
return userDao.getById(id);
} public void insert(User user) {
Date now = new Date();
user.setCreateTime(now);
user.setModifyTime(now);
userDao.insert(user);
} public void update(User user) {
user.setModifyTime(new Date());
userDao.update(user);
} }

自己在网上找的时候不是全的,包括上文注释中提到的出现的问题,也是根据错误提示多个DataSource目标,以及没设置就是没有DataSource了。

PS:其实之前一直以为DataSource听起来挺悬乎,没去细想,当然主要由于自己是半路出家的Java、web开发,本身也没那么熟悉,所以没理解哈,

现在想想DataSource其实就是保存了些配置,说白了是url和账号密码,就是连接数据库的,相当于你用命令行连接了数据库进行了操作一样,各种

数据库DataSource的实现高功能多半应该是做了些连接池的管理,以及连接的打开关闭之类,其实实质上我觉得应该就是说最后用的就是那个url加

上账号密码就能连接并操作了。这样的话,多数据源的切换就好理解了,结合 aop 在函数入口之前设置好当前线程数据源,以及根据路由数据库类

AbstractRoutingDataSource将选择数据源留给子类实现的方法

determineCurrentLookupKey,从而在service方法入口设置数据源,在使用时取到数据源。

大PS:这应该算是我写的最全的一次Java的博客了!!!




最新文章

  1. Ajax及跨域
  2. [转]实现文字跑马灯效果,scrolling text from right to left
  3. PARENT VIEW INVALIDATE
  4. Javascript对象创建
  5. SQLServer索引
  6. 【ASP.NET Web API教程】6.4 模型验证
  7. 第一章 01 namespace 命名空间
  8. ARP 实现
  9. BZOJ_1001_狼抓兔子_(平面图求最小割+对偶图求最短路)
  10. xmlns:tools=&quot;http://schemas.android.com/tools&quot;以及tools:context=&quot;.ConfActivity&quot;是什么意思
  11. HDU 5920 Ugly Problem 【模拟】 (2016中国大学生程序设计竞赛(长春))
  12. java Socket 长连接 心跳包 客户端 信息收发 demo
  13. 在cygwin下编译c语言
  14. memcached在注册表的位置
  15. Leetcode_257_Binary Tree Paths
  16. protel项目创建
  17. 关于null的判断
  18. python中的集合
  19. sql 存储过程参数为空则不作为条件
  20. LNMP(一)

热门文章

  1. C#获取枚举的描述
  2. k8s 常用指令
  3. iOS Png Crush 错误
  4. Python----初次见面,请多关照!
  5. Linux中的任务调度
  6. java日期时间
  7. leetcode-268-Missing Number(异或)
  8. jee-oxygen版eclipse的安装与卸载 maven配置
  9. 编程开发之--java多线程学习总结(4)
  10. ElasticSearch 系列随笔