一般情况下网站对数据库的读要比写多多了,所以当数据量大了的时候,使用读写分离是很有必要的

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配置主从复制双库实现读写分离相关推荐

  1. springboot集成swagger2多模块中文配置详细步骤,解决集成mybatis或mybatis-plus无法正常使用问题

    springboot集成swagger2多模块中文配置详细步骤,解决集成mybatis或mybatis-plus无法正常使用问题 参考文章: (1)springboot集成swagger2多模块中文配 ...

  2. Spring集成Mybatis配置映射文件方法详解

    Spring ORM模块集成Mybatis使用到了mybatis-spring,在配置mybatis映射文件的时候,一般不直接在Mybatis的配置文件里进行配置,而会在Spring的配置文件里使用M ...

  3. 学习Spring Boot:(七)集成Mybatis

    前面都是用的是spring data JPA,现在学习下Mybatis,而且现在Mybatis也像JPA那样支持注解形式了,也非常方便,学习一下. 数据库 mysql 5.7 添加依赖 在pom文件中 ...

  4. mybatis支持驼峰自动转换sql吗_SpringBoot整合mybatis——配置mybatis驼峰命名规则自动转换...

    一.简述 mybatis驼峰式命名规则自动转换: 使用前提:数据库表设计按照规范"字段名中各单词使用下划线"_"划分": 使用好处:省去mapper.xml文件 ...

  5. Spring Boot(二): 集成Mybatis

    上一篇讲述了什么是Spring Boot.如何创建Sping Boot项目以及如何通过配置修改端口号: 本篇将讲述Spring Boot与Mybatis的整合过程. 一.添加mybatis以及mysq ...

  6. 在IntelliJ IDEA中使用Spring Initializr创建项目以及Maven集成与配置

    场景: 1.IntelliJ IDEA是高度智能化的IDE,提高开发效率       2.Maven作为项目管理工具,使用场景特别多 1.打开IDEA   点击 File/New Project,进入 ...

  7. Visual Studio 2022 IDE 下载安装与环境配置,C语言/C++集成环境配置,VS2022。详细环境配置教程,最适合写c语言的编译器

    编者今天不小心把VS2019的一个文件删除后迫不得已安装了VScode,然后在运行程序时总是提示错误,在尝试了一个小时的修错后终于发现,还是VS年份系列香,什么破VScode,老子不用你了!于是就有了 ...

  8. MyCat 之路 | 配置 Mysql 读写分离+强制走写节点+根据主从延时的读写分离

    数据库读写分离对于大型系统或者访问量很高的互联网应用来说,是必不可少的一个重要功能.对于MySQL来说,标准的读写分离是主从模式,一个写节点Master后面跟着多个读节点,读节点的数量取决于系统的压力 ...

  9. ssm-spring集成mybatis事务

    ssm-spring集成mybatis事务 事务 MyBatis-Spring库的引入,无需创建新的MyBatis事务管理器,就能使MyBatis接入到Spring事. 引入的方式既可以是注解,也可以 ...

最新文章

  1. Citrix XenApp 下载及一年 developer license 获取
  2. 牛津英语3a_空中课堂 | 牛津英语学霸笔记3A M3U2
  3. MaxCompute实践分析
  4. 用java智能锁远程,从生产者-消费者模型了解线程、同步、锁(java)
  5. 在虚拟机上安装redis集群,redis使用版本为4.0.5,本机通过命令客户端可以连接访问,外部主机一直访问不了...
  6. 艾伟_转载:.NET 4.0新特性-- Corrupted State Exceptions
  7. Manjaro 17 搭建 redis 4.0.1 集群服务
  8. 朋友圈发图多大不会被压缩_类风湿会不会引发肾病?会!本文告诉你对内脏的伤害有多大...
  9. 详解收发不畅原因及U-Mail邮件中继解决之道
  10. [Android] AsyncTask详解
  11. 再谈javascript图片预加载技术(转)
  12. QTTabBar v1039
  13. 阿里云域名注册及域名解析
  14. Github渗透测试工具库
  15. python设计报告的前言怎么写_前  言_Python语言程序设计_红黑联盟读书频道
  16. python获取声音波形的关键特征
  17. Error response from daemon conflict unable to delete 3f37e5daf5bd (cannot be forced) - image is bein
  18. c语言str系列函数
  19. 使用ScanShadowsFilter过滤激光雷达拖尾
  20. UVa1601万圣节后的早晨

热门文章

  1. 强制删除 bat命令
  2. 计算机毕业设计net综合型体育场馆管理系统(系统+数据库+源码+文档)
  3. Spring Cloud微服务全家桶主要组件及简要介绍
  4. 武汉学员,我们和传智播客boss在一起
  5. matlab中liccode函数,基于matlab车牌识别系统设计与实现(最终版)
  6. funannotate安装
  7. Python数据可视化神奇利器,Pyecharts的使用(1.柱状图使用之分析LPL春季赛职业选手数据可视化)
  8. 分屏多窗开窗画中画多视图播放器
  9. PYTHON多线程行情抓取工具实现
  10. 用计算机寻找素数,找出1到1000所有质数(电脑流程图)