是从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. Linux下source文件两种方法

    1.直接source命令加文件 source /etc/rc.d/init.d/functions 2.点(.)加文件 . /etc/rc.d/init.d/functions

  2. (zxing.net)一维码Code 128的简介、实现与解码

    一.简介 一维码Code 128:1981年推出,是一种长度可变.连续性的字母数字条码.与其他一维条码比较起来,相对较为复杂,支持的字元也相对较多,又有不同的编码方式可供交互运用,因此其应用弹性也较大 ...

  3. windows 10 自适应布局

    https://msdn.microsoft.com/library/windows/apps/dn894631.aspx Use visual state triggers to build UI ...

  4. 【OCP-12c】2019年CUUG OCP 071考试题库(76题)

    76.View the exhibit and examine the description of the DEPARTMENTSand EMPLOYEEStables. The retrieve ...

  5. forword和重定向有什么区别?

    一.forword 1.请求只能从同一个web中转发,当前应用之外的找不到. 2.地址栏不变. 3.共享request对象,请求链不断. 4.永久性跳转,最常用. 5.客户端方法功能跳转 二.重定向 ...

  6. 源码编译安装MySQL-5.6/mysql-5.6.39------踩了无数坑,重装了十几次服务器才会的,不容易啊!

    1.切换到src目录 cd /usr/local/src/ 2. 下载mysql免编译二进制包 免编译的mysql二进制包5.6源码包: wget http://mirrors.163.com/mys ...

  7. Python(序列化json,pickle,shelve)

    序列化 参考:https://www.cnblogs.com/yuanchenqi/articles/5732581.html # dic = str({'1':'111'}) # # f = ope ...

  8. ELK安装成windows服务

    一.Elasticsearch安装成windows服务 我的es所在路径为:D:\ELK5.5.0\elasticsearch-5.5.0 Java 安装目录为:C:\Program Files\Ja ...

  9. 总结day7 ---- 函数的内容 ,初识,返回值,进阶(一)

    内容大纲: 一: 函数识别 二: 函数的结构 三: 函数的返回值, 四: 函数的参数 五: 动态参数 六: 形参的顺序 七: 名称空间 八: 作用域 九: 加载顺序和取值顺序 十: 内置函数 十一: ...

  10. python --爬虫基础 --爬取今日头条 使用 requests 库的基本操作, Ajax

    '''思路一: 由于是Ajax的网页,需要先往下划几下看看XHR的内容变化二:分析js中的代码内容三:获取一页中的内容四:获取图片五:保存在本地 使用的库1. requests 网页获取库 2.fro ...