springboot主从数据库
是从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主从数据库的更多相关文章
- SpringBoot数据库读写分离之基于Docker构建主从数据库同步实例
看了好久的SpringBoot结合MyBatista实现读写,但是一直没有勇气实现他,今天终于接触到了读写分离的东西,读写分离就是讲读操作执行在Slave数据库(从数据库),写操作在Master数据库 ...
- 2018-01-08 学习随笔 SpirngBoot整合Mybatis进行主从数据库的动态切换,以及一些数据库层面和分布式事物的解决方案
先大概介绍一下主从数据库是什么?其实就是两个或N个数据库,一个或几个主负责写(当然也可以读),另一个或几个从只负责读.从数据库要记录主数据库的具体url以及BigLOG(二进制日志文件)的参数.原理就 ...
- Spring主从数据库的配置和动态数据源切换原理
原文:https://www.liaoxuefeng.com/article/00151054582348974482c20f7d8431ead5bc32b30354705000 在大型应用程序中,配 ...
- (转)SqlServer 数据库同步的两种方式 (发布、订阅),主从数据库之间的同步
最近在琢磨主从数据库之间的同步,公司正好也需要,在园子里找了一下,看到这篇博文比较详细,比较简单,本人亲自按步骤来过,现在分享给大家. 在这里要提醒大家的是(为了更好的理解,以下是本人自己理解,如有错 ...
- mysql主从数据库不同步的2种解决方法(转)
今天发现Mysql的主从数据库没有同步 先上Master库: mysql>show processlist; 查看下进程是否Sleep太多.发现很正常. show master status; ...
- MySQL主从数据库同步延迟问题解决(转)
最近在做MySQL主从数据库同步测试,发现了一些问题,其中主从同步延迟问题是其中之一,下面内容是从网上找到的一些讲解,记录下来以便自己学习: MySQL的主从同步是一个很成熟的架构,优点为:①在从服务 ...
- mysql 主从数据库设置方法
1.主从数据库都需开启bin-log日志 2.在my.ini(windows)或my.cnf(linux)配置文件中添加 server-id = 1(主从配置 id 必须不同) 例子: [mysqld ...
- Yii2 主从 数据库
配置方法 参考资料:http://www.linuxidc.com/Linux/2015-07/120134.htm 读写分离(Read/Write Splitting). 1.原理:让主数据库(ma ...
- Spring JDBC主从数据库配置
通过昨天学习的自定义配置注释的知识,探索了解一下web主从数据库的配置: 背景:主从数据库:主要是数据上的读写分离: 数据库的读写分离的好处? 1. 将读操作和写操作分离到不同的数据库上,避免主服务器 ...
随机推荐
- 谁说他们版本不兼容——hadoop1.2.1+hbase0.94.11+nutch2.2.1+el
一.背景 最近由于项目和论文的需要,需要搭建一个垂直搜索的环境,查阅了很多资料,决定使用Apache的一套解决方案hadoop+hbase+nutch+es.这几样神器的作用就不多作介绍了,自行参考各 ...
- Linux中连接mysql执行sql文件
数据量小的时候可以把sql语句内容粘贴执行,但是文件很大的时候,这样执行效率很慢很慢,需要使用source执行sql文件 1.客户端连接mysql数据库 [root@iZbp1bb2egi7w0uey ...
- django系列3.1--url路由配置, 正则, 分发include, 分组命名匹配
一.url配置 在django项目中urls.py文件中就是为这个url调用的view(视图)函数之间的映射表,来配置访问的一个url执行什么代码 默认的基本格式: from django.conf. ...
- ssh 使用密钥无法登入Linux系统
今天测试密钥登入linux系统时 出现如下问题: root@compute01:~# ssh alicxxx@xxx.com -p -i alickicxxxxxxx.key @@@@@@@@@@@@ ...
- (转)科普:SATA、PCIe、AHCI、NVMe
原文链接:https://forum.51nb.com/thread-1670848-1-1.html IT 界总喜欢发明新名词.而且同一个东西,可能有几个不同的名字.同一个名字,又可能指不同的东西. ...
- git 使用merge 对本地分支进行合并 并进行代码提交的流程
1.只有当将修改内容commit后 该修改才完全生效,进行merge前需要将两个分支修改的内容都进行commit 2.假设本地两个分支 用于开发的分支:dev 用于同步远程仓库的分支:mas ...
- Java最常见的200+面试题及自己梳理的答案--面试必备(一)
昨天在今日头条上看到一份所谓经常面别人的TL梳理的面试题,看着比较完善,但是,没有对应的答案,自己看着研究学习了下,顺带梳理下答案.主要包括以下模块:Java基础.容器.多线程.反射.对象拷贝.Jav ...
- HDU-6125-Friend-Graph-2017CCPC网络赛(图论,拉姆齐定理-组合数学)
Friend-Graph Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) To ...
- 架构师养成记--18.NIO
有人叫new IO 我这里就叫Non-block IO 经典概念: Buffer(缓冲区):之前直接通过流,现在提供一个buffer存放数据. Channel:管道,包括ServerSocketCha ...
- 01背包-记忆化搜索到成型的DP
记忆化搜索 #include<bits/stdc++.h> using namespace std; typedef long long ll; int n,W; int dp[105][ ...