spring配置主库从库_springboot集成mybatis配置主从复制双库实现读写分离
一般情况下网站对数据库的读要比写多多了,所以当数据量大了的时候,使用读写分离是很有必要的
spring提供了数据源路由的类,正好拿它来实现一下
创建项目
简单的springboot项目,依赖有mybatis,mysql,aspect
springboot版本是 2.2.0.RELEASE
org.springframework.boot
spring-boot-starter
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.1.1
mysql
mysql-connector-java
runtime
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-aop
数据
我测试的两个库,一个主库一个从库,只有一个表,数据如下
CREATE TABLE `user` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- 主库的数据
INSERT INTO `user` (`id`, `username`)
VALUES
(1, 'tomoya'),
(2, '朋也');
-- 从库的数据
INSERT INTO `user` (`id`, `username`)
VALUES
(1, 'tomoya');
可以看到从库中少了一条数据,这样可以对比看读写分离的功能是否生效了
配置
首先就是springboot的双数据源的配置
application.yml
spring:
datasource:
master:
jdbc-url: jdbc:mysql://192.168.16.87:3306/pybbs?useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: 123123
driver-class-name: com.mysql.cj.jdbc.Driver
slave:
jdbc-url: jdbc:mysql://192.168.16.109:3306/pybbs?useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: 123123
driver-class-name: com.mysql.cj.jdbc.Driver
使用java代码来创建两个数据源, 创建类 MyDataSource.java
@Configuration
public class MyDataSource {
// 主库数据源
@Bean
@ConfigurationProperties("spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
// 从库数据源
@Bean
@ConfigurationProperties("spring.datasource.slave")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
}
// 数据源路由
@Bean
public DataSource dynamicDatasource() {
Map dataSourceMap = new HashMap<>();
dataSourceMap.put(MultipleDataSourceHelper.MASTER, masterDataSource());
dataSourceMap.put(MultipleDataSourceHelper.SLAVE, slaveDataSource());
DynamicDataSource dds = new DynamicDataSource();
dds.setTargetDataSources(dataSourceMap);
dds.setDefaultTargetDataSource(masterDataSource());
return dds;
}
}
其中数据源路由是一个自定义的类,继承了spring提供的类AbstractRoutingDataSource,然后实现里面的一个方法determineCurrentLookupKey() 即可,这个方法返回的值就是用哪个数据源
查看类AbstractRoutingDataSource可以看到,里面有两个变量
private Map targetDataSources;
private Object defaultTargetDataSource;
targetDataSources是一个数据库的map集合,key是自定义的,可以随便指定,value就是配置好的数据源,如下代码
defaultTargetDataSource是默认的数据源,如果指定的key在targetDataSources里没有找到对应的value,那么就会使用这个默认的数据源,防止程序出错
Map dataSourceMap = new HashMap<>();
dataSourceMap.put(MultipleDataSourceHelper.MASTER, masterDataSource());
dataSourceMap.put(MultipleDataSourceHelper.SLAVE, slaveDataSource());
DynamicDataSource dds = new DynamicDataSource();
dds.setTargetDataSources(dataSourceMap);
dds.setDefaultTargetDataSource(masterDataSource());
既然是一个map,value是数据源,那key最好建一个类管理起来比较好
public class MultipleDataSourceHelper {
public static final String MASTER = "master";
public static final String SLAVE = "slave";
private static ThreadLocal contextHolder = new ThreadLocal<>();
public static void set(String db) {
contextHolder.set(db);
}
public static String get() {
return contextHolder.get();
}
}
实现数据源路由
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return MultipleDataSourceHelper.get();
}
}
可以看到这里动态指定的数据源是从自定义管理key的类中取的,所以要想让CRUD的操作走主库还是从库就只需要改变MultipleDataSourceHelper 里的 contextHolder 就行了
mybatis配置
如果是单数据源,利用spring的配置文件就可以了,也就不用单独对mybatis进行配置了,这里数据源我是自己配置的,所以还要单独配置一下mybatis,给它指定一个数据源,如下
@Configuration
public class MyBatisConfig {
@Autowired
private DataSource dynamicDatasource;
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
// 给mybatis指定上面配置好的动态数据源
sqlSessionFactoryBean.setDataSource(dynamicDatasource);
// 自己配置mybatis的话,这个必须要指定mapper位置,在application.yml里配置的不会生效了
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*Mapper.xml"));
return sqlSessionFactoryBean.getObject();
}
}
如果用application.yml配置的话,只需要在 Mapper 上加一个注解即可 @Mapper 但自己配置的mybatis就要扫包了,在启动类上添加注解 @MapperScan("com.example.multipledatasource.mapper")
有了这个注解,Mapper上的 @Mapper 就不用要了
实体类,mapper
这里我用的实体类mapper跟上一篇博客是一样的,下面再贴一下吧
User.java
public class User {
private Integer id;
private String username;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
'}';
}
}
UserMapper.java
//@Mapper
public interface UserMapper {
// 注解方式查询
@Select("select * from user;")
List selectAll();
// xml方式查询
List selectAllWithXml();
}
UserMapper.xml
select *
from user;
测试
@SpringBootTest
public class MultipleDatasourceApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void contextLoads() {
// 指定使用主库
MultipleDataSourceHelper.set(MultipleDataSourceHelper.MASTER);
List users = userMapper.selectAll();
for (User user : users) {
System.out.println(user.toString());
}
System.out.println("=========================");
// 指定使用从库
MultipleDataSourceHelper.set(MultipleDataSourceHelper.SLAVE);
List users1 = userMapper.selectAllWithXml();
for (User user : users1) {
System.out.println(user.toString());
}
}
}
结果
2019-10-30 10:47:07.438 INFO 26478 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2019-10-30 10:47:07.644 INFO 26478 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
User{id=1, username='tomoya'}
User{id=2, username='朋也'}
=========================
2019-10-30 10:47:07.691 INFO 26478 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Starting...
2019-10-30 10:47:07.738 INFO 26478 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Start completed.
User{id=1, username='tomoya'}
注解实现
这样手动设置比较麻烦,可以自定义一个注解,在读的方法的上通过注解指定从库查询,在写的方法上使用注解指定写入主库
spring两大思想中的aop可以实现这个功能,首先创建一个切入点类
public class DSPointcut {
@Pointcut("execution(public * com.example.multipledatasource.mapper.*.*(..))")
public void selectorDSPointcut() {
}
}
有了切入点了,还少了注解,下面自定义一个注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DSSelector {
String value();
}
在UserMapper.java类里的两个方法上加上相应的注解
//@Mapper
public interface UserMapper {
// 注解方式查询
@Select("select * from user;")
@DSSelector(MultipleDataSourceHelper.MASTER) // 指定从主库查询
List selectAll();
// xml方式查询
@DSSelector(MultipleDataSourceHelper.SLAVE) // 指定从从库查询
List selectAllWithXml();
}
我这写的是两个查询方法,当然也可以是插入,更新,删除,只需要一个注解指定一下就行了
有了注解,有了切入点了,下面就是切入mapper方法执行前去获取这个方法上的注解信息了,然后再去设置MultipleDataSourceHelper,这样就可以实现自动了,代码如下
@Component
@Aspect
public class DSSelectorImpl {
@Before("com.example.multipledatasource.aspect.DSPointcut.selectorDSPointcut()")
public void changeDS(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
DSSelector selector = method.getAnnotation(DSSelector.class);
if (selector == null) return;
MultipleDataSourceHelper.set(selector.value());
}
}
再测试
@SpringBootTest
@EnableAspectJAutoProxy
public class MultipleDatasourceApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void contextLoads() {
List users = userMapper.selectAll();
for (User user : users) {
System.out.println(user.toString());
}
System.out.println("=========================");
List users1 = userMapper.selectAllWithXml();
for (User user : users1) {
System.out.println(user.toString());
}
}
}
日志
2019-10-30 10:56:11.895 INFO 26991 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2019-10-30 10:56:12.120 INFO 26991 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
User{id=1, username='tomoya'}
User{id=2, username='朋也'}
=========================
2019-10-30 10:56:12.167 INFO 26991 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Starting...
2019-10-30 10:56:12.211 INFO 26991 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Start completed.
User{id=1, username='tomoya'}
跟手动指定的结果是一样的
原文链接: https://tomoya92.github.io/2019/10/30/spring-boot-mybatis-read-write-separation/
spring配置主库从库_springboot集成mybatis配置主从复制双库实现读写分离相关推荐
- springboot集成swagger2多模块中文配置详细步骤,解决集成mybatis或mybatis-plus无法正常使用问题
springboot集成swagger2多模块中文配置详细步骤,解决集成mybatis或mybatis-plus无法正常使用问题 参考文章: (1)springboot集成swagger2多模块中文配 ...
- Spring集成Mybatis配置映射文件方法详解
Spring ORM模块集成Mybatis使用到了mybatis-spring,在配置mybatis映射文件的时候,一般不直接在Mybatis的配置文件里进行配置,而会在Spring的配置文件里使用M ...
- 学习Spring Boot:(七)集成Mybatis
前面都是用的是spring data JPA,现在学习下Mybatis,而且现在Mybatis也像JPA那样支持注解形式了,也非常方便,学习一下. 数据库 mysql 5.7 添加依赖 在pom文件中 ...
- mybatis支持驼峰自动转换sql吗_SpringBoot整合mybatis——配置mybatis驼峰命名规则自动转换...
一.简述 mybatis驼峰式命名规则自动转换: 使用前提:数据库表设计按照规范"字段名中各单词使用下划线"_"划分": 使用好处:省去mapper.xml文件 ...
- Spring Boot(二): 集成Mybatis
上一篇讲述了什么是Spring Boot.如何创建Sping Boot项目以及如何通过配置修改端口号: 本篇将讲述Spring Boot与Mybatis的整合过程. 一.添加mybatis以及mysq ...
- 在IntelliJ IDEA中使用Spring Initializr创建项目以及Maven集成与配置
场景: 1.IntelliJ IDEA是高度智能化的IDE,提高开发效率 2.Maven作为项目管理工具,使用场景特别多 1.打开IDEA 点击 File/New Project,进入 ...
- Visual Studio 2022 IDE 下载安装与环境配置,C语言/C++集成环境配置,VS2022。详细环境配置教程,最适合写c语言的编译器
编者今天不小心把VS2019的一个文件删除后迫不得已安装了VScode,然后在运行程序时总是提示错误,在尝试了一个小时的修错后终于发现,还是VS年份系列香,什么破VScode,老子不用你了!于是就有了 ...
- MyCat 之路 | 配置 Mysql 读写分离+强制走写节点+根据主从延时的读写分离
数据库读写分离对于大型系统或者访问量很高的互联网应用来说,是必不可少的一个重要功能.对于MySQL来说,标准的读写分离是主从模式,一个写节点Master后面跟着多个读节点,读节点的数量取决于系统的压力 ...
- ssm-spring集成mybatis事务
ssm-spring集成mybatis事务 事务 MyBatis-Spring库的引入,无需创建新的MyBatis事务管理器,就能使MyBatis接入到Spring事. 引入的方式既可以是注解,也可以 ...
最新文章
- Citrix XenApp 下载及一年 developer license 获取
- 牛津英语3a_空中课堂 | 牛津英语学霸笔记3A M3U2
- MaxCompute实践分析
- 用java智能锁远程,从生产者-消费者模型了解线程、同步、锁(java)
- 在虚拟机上安装redis集群,redis使用版本为4.0.5,本机通过命令客户端可以连接访问,外部主机一直访问不了...
- 艾伟_转载:.NET 4.0新特性-- Corrupted State Exceptions
- Manjaro 17 搭建 redis 4.0.1 集群服务
- 朋友圈发图多大不会被压缩_类风湿会不会引发肾病?会!本文告诉你对内脏的伤害有多大...
- 详解收发不畅原因及U-Mail邮件中继解决之道
- [Android] AsyncTask详解
- 再谈javascript图片预加载技术(转)
- QTTabBar v1039
- 阿里云域名注册及域名解析
- Github渗透测试工具库
- python设计报告的前言怎么写_前 言_Python语言程序设计_红黑联盟读书频道
- python获取声音波形的关键特征
- Error response from daemon conflict unable to delete 3f37e5daf5bd (cannot be forced) - image is bein
- c语言str系列函数
- 使用ScanShadowsFilter过滤激光雷达拖尾
- UVa1601万圣节后的早晨
热门文章
- 强制删除 bat命令
- 计算机毕业设计net综合型体育场馆管理系统(系统+数据库+源码+文档)
- Spring Cloud微服务全家桶主要组件及简要介绍
- 武汉学员,我们和传智播客boss在一起
- matlab中liccode函数,基于matlab车牌识别系统设计与实现(最终版)
- funannotate安装
- Python数据可视化神奇利器,Pyecharts的使用(1.柱状图使用之分析LPL春季赛职业选手数据可视化)
- 分屏多窗开窗画中画多视图播放器
- PYTHON多线程行情抓取工具实现
- 用计算机寻找素数,找出1到1000所有质数(电脑流程图)