是从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的博客了!!!




springboot主从数据库的更多相关文章

  1. SpringBoot数据库读写分离之基于Docker构建主从数据库同步实例

    看了好久的SpringBoot结合MyBatista实现读写,但是一直没有勇气实现他,今天终于接触到了读写分离的东西,读写分离就是讲读操作执行在Slave数据库(从数据库),写操作在Master数据库 ...

  2. 2018-01-08 学习随笔 SpirngBoot整合Mybatis进行主从数据库的动态切换,以及一些数据库层面和分布式事物的解决方案

    先大概介绍一下主从数据库是什么?其实就是两个或N个数据库,一个或几个主负责写(当然也可以读),另一个或几个从只负责读.从数据库要记录主数据库的具体url以及BigLOG(二进制日志文件)的参数.原理就 ...

  3. Spring主从数据库的配置和动态数据源切换原理

    原文:https://www.liaoxuefeng.com/article/00151054582348974482c20f7d8431ead5bc32b30354705000 在大型应用程序中,配 ...

  4. (转)SqlServer 数据库同步的两种方式 (发布、订阅),主从数据库之间的同步

    最近在琢磨主从数据库之间的同步,公司正好也需要,在园子里找了一下,看到这篇博文比较详细,比较简单,本人亲自按步骤来过,现在分享给大家. 在这里要提醒大家的是(为了更好的理解,以下是本人自己理解,如有错 ...

  5. mysql主从数据库不同步的2种解决方法(转)

    今天发现Mysql的主从数据库没有同步 先上Master库: mysql>show processlist; 查看下进程是否Sleep太多.发现很正常. show master status; ...

  6. MySQL主从数据库同步延迟问题解决(转)

    最近在做MySQL主从数据库同步测试,发现了一些问题,其中主从同步延迟问题是其中之一,下面内容是从网上找到的一些讲解,记录下来以便自己学习: MySQL的主从同步是一个很成熟的架构,优点为:①在从服务 ...

  7. mysql 主从数据库设置方法

    1.主从数据库都需开启bin-log日志 2.在my.ini(windows)或my.cnf(linux)配置文件中添加 server-id = 1(主从配置 id 必须不同) 例子: [mysqld ...

  8. Yii2 主从 数据库

    配置方法 参考资料:http://www.linuxidc.com/Linux/2015-07/120134.htm 读写分离(Read/Write Splitting). 1.原理:让主数据库(ma ...

  9. Spring JDBC主从数据库配置

    通过昨天学习的自定义配置注释的知识,探索了解一下web主从数据库的配置: 背景:主从数据库:主要是数据上的读写分离: 数据库的读写分离的好处? 1. 将读操作和写操作分离到不同的数据库上,避免主服务器 ...

随机推荐

  1. [Erlang33]使用recon从网页查看Erlang运行状态

    0.需求分析 Erlang最好的卖点之一就是提供了一个非常强大的shell来查看Node运行时的各种状态,可以进行各种各样的内部查看,在运行时调试和分析,热更新代码.   但是总有一些在生产环境下要慎 ...

  2. UWP开发入门(二)——RelativePanel

    RelativePanel也是Win10 UWP新增的控件,和上篇提到的SplitView一样在UWP的UI布局起到非常重要的作用.说句实在话,这货其实就是为了UWP的Adaptive UI而特意增加 ...

  3. linux 动态库加载路径修改

    1.在 /etc/ld.so.conf 文件中添加搜索路径,重启或者 ldconfig 生效: 2.在 /etc/ld.so.conf.d 目录下添加 *.conf 文件,其中可以添加搜索路径,重启获 ...

  4. android frame的学习

    http://blog.csdn.net/littletigerat/article/details/6318365   //2011年Android Activity生命周期学习笔记 http:// ...

  5. 快速排序 JAVA实现

    快速排序 每次排序的时候设置一个基准点,将小于等于基准点的数全部放到基准点的左边,将大于等于基准点的数全部放到基准点的右边.快速排序是不稳定的,时间复杂度(平均):nlogn public class ...

  6. 【ocp-12c】最新Oracle OCP-071考试题库(39题)

    39.choose the best answer View the Exhibit and examine the description of the EMPLOYEES table. You w ...

  7. eclipse打包jar文件

    论文仿真做线性回归分类在人脸识别中应用与研究,在单机下实现LRC算法后,又在Hadoop云平台下实现了该算法.在比较实验结果时候需要放在相同硬件条件下比较.但是LRC单机算法是在windows下的ec ...

  8. [ActionScript 3.0] as3启动QQ

    import flash.html.HTMLLoader; import flash.net.URLLoader; import flash.net.URLRequest; import flash. ...

  9. 2016级算法期末上机-C.简单·Bamboo's Fight with DDLs III

    简单·Bamboo's Fight with DDLs III 分析 一句话:贪心,简单哈夫曼应用,要求的其实是所有结点的值与权值的乘积之和,也就是带权路径长. 可以理解为非叶子节点的权值的和,这里的 ...

  10. 采用prometheus 监控mysql

    1. prometheus 是什么 开源的系统监控和报警工具,监控项目的流量.内存量.负载量等实时数据. 它通过直接或短时jobs中介收集监控数据,在本地存储所有收集到的数据,并且通过定义好的rule ...