大家好,我是烤鸭:

今天分享springboot读写分离配置。

   环境:

 springboot  2.1.0.RELEASE

         场景说明,目前的需求是 读数据源 * 2 + 写数据源 * 1

1.    配置文件

application.yml

server:port: 8085
spring:application:name: test-data-testdatasource:write:jdbc-url: jdbc:mysql://localhost:3306/testusername: rootpassword: test.Devdriver-class-name: com.mysql.jdbc.Drivertype: com.zaxxer.hikari.HikariDataSource   connectionTimeout: 30000validationTimeout: 5000maxPoolSize: 200minIdle: 100readaw:jdbc-url: jdbc:mysql://localhost:3306/testusername: rootpassword: test!idriver-class-name: com.mysql.jdbc.Drivertype: com.zaxxer.hikari.HikariDataSource   connectionTimeout: 30000validationTimeout: 5000maxPoolSize: 200minIdle: 100readdc:jdbc-url: jdbc:mysql://localhost:3306/testusername: rootpassword: test!idriver-class-name: com.mysql.jdbc.Drivertype: com.zaxxer.hikari.HikariDataSource   connectionTimeout: 30000validationTimeout: 5000maxPoolSize: 200minIdle: 100
#mybatis
mybatis:###把xml文件放在com.XX.mapper.*中可能会出现找到的问题,这里把他放在resource下的mapper中mapper-mapperLocations: classpath*:mapper/**/**/*.xmltype-aliases-package: com.test.test.pojoconfiguration:map-underscore-to-camel-case: truecache-enabled: falsecall-setters-on-nulls: trueuseGeneratedKeys: true

2.    配置类

DataSourceConfig.java

默认 读数据源,如果需要增加或者减少数据源需要修改 myRoutingDataSource 方法中的参数

package com.test.test.config.db;import com.test.test.datasource.MyRoutingDataSource;
import com.test.test.datasource.enums.DBTypeEnum;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;/*** 关于数据源配置,参考SpringBoot官方文档第79章《Data Access》* 79. Data Access* 79.1 Configure a Custom DbSource* 79.2 Configure Two DataSources*/@Configuration
public class DataSourceConfig {@AutowiredEnvironment environment;@Bean@ConfigurationProperties("spring.datasource.readaw")public DataSource readDataSourceAw() {DataSource build = DataSourceBuilder.create().build();HikariDataSource hikariDataSource = buildDataSource(build,"readaw");return hikariDataSource;}@Bean@ConfigurationProperties("spring.datasource.readdc")public DataSource readDataSourceDc() {DataSource build = DataSourceBuilder.create().build();HikariDataSource hikariDataSource = buildDataSource(build,"readdc");return hikariDataSource;}@Bean@ConfigurationProperties("spring.datasource.write")public DataSource writeDataSource() {DataSource build = DataSourceBuilder.create().build();HikariDataSource hikariDataSource = buildDataSource(build,"write");return hikariDataSource;}@Beanpublic DataSource myRoutingDataSource(@Qualifier("readDataSourceAw") DataSource readDataSourceAw,@Qualifier("readDataSourceDc") DataSource readDataSourceDc,@Qualifier("writeDataSource") DataSource writeDataSource) {Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put(DBTypeEnum.READ_AW, readDataSourceAw);targetDataSources.put(DBTypeEnum.READ_DC, readDataSourceDc);targetDataSources.put(DBTypeEnum.WRITE, writeDataSource);MyRoutingDataSource myRoutingDataSource = new MyRoutingDataSource();myRoutingDataSource.setDefaultTargetDataSource(readDataSourceAw);myRoutingDataSource.setTargetDataSources(targetDataSources);return myRoutingDataSource;}public HikariDataSource buildDataSource(DataSource dataSource,String dataSourcePrefix){HikariDataSource hikariDataSource= (HikariDataSource) dataSource;hikariDataSource.setDriverClassName(environment.getProperty("spring.datasource."+dataSourcePrefix+".driver-class-name"));hikariDataSource.setJdbcUrl(environment.getProperty("spring.datasource."+dataSourcePrefix+".jdbc-url"));hikariDataSource.setUsername(environment.getProperty("spring.datasource."+dataSourcePrefix+".username"));hikariDataSource.setPassword(environment.getProperty("spring.datasource."+dataSourcePrefix+".password"));hikariDataSource.setMinimumIdle(Integer.parseInt(environment.getProperty("spring.datasource."+dataSourcePrefix+".minIdle")));hikariDataSource.setConnectionTimeout(Long.parseLong(environment.getProperty("spring.datasource."+dataSourcePrefix+".connectionTimeout")));hikariDataSource.setValidationTimeout(Long.parseLong(environment.getProperty("spring.datasource."+dataSourcePrefix+".validationTimeout")));hikariDataSource.setMaximumPoolSize(Integer.parseInt(environment.getProperty("spring.datasource."+dataSourcePrefix+".maxPoolSize")));return hikariDataSource;}
}

MyBatisConfig.java

注意映射mapper文件路径是在这里修改的,因为重新注入了sqlSession, yml中配置的无效

package com.test.test.config.mybatis;import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;import javax.annotation.Resource;
import javax.sql.DataSource;@EnableTransactionManagement
@Configuration
public class MyBatisConfig {@Resource(name = "myRoutingDataSource")private DataSource myRoutingDataSource;@Beanpublic SqlSessionFactory sqlSessionFactory() throws Exception {SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(myRoutingDataSource);sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/**/*.xml"));return sqlSessionFactoryBean.getObject();}@Beanpublic PlatformTransactionManager platformTransactionManager() {return new DataSourceTransactionManager(myRoutingDataSource);}
}

DataSourceAop.java

aop配置类,通过aop的方式限制哪个service的方法连接哪个数据源
目前是根据类上的注解来判断,可以修改为根据方法的注解来判断走哪个数据源

package com.test.test.datasource.aop;import com.test.test.datasource.annotation.DbSource;
import com.test.test.datasource.handler.DBContextHolder;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;@Aspect
@Component
public class DataSourceAop {/*** 另一种写法:if...else...  判断哪些需要读从数据库,其余的走主数据库*/@Before("execution(* com.test.test.service.impl.*.*(..))")public void before(JoinPoint jp){MethodSignature methodSignature = (MethodSignature) jp.getSignature();Method method = methodSignature.getMethod();System.out.println("拦截到了" + jp.getSignature().getName() +"方法...");Class<?> targetClass = jp.getTarget().getClass();boolean flag = targetClass.isAnnotationPresent(DbSource.class);//包含数据源注解,数据源为注解中的类if(flag){//获取注解的valueDbSource annotation = targetClass.getAnnotation(DbSource.class);String value = annotation.value();DBContextHolder.read(value);}else {//不包含注解,查询方法默认走 默认读数据源if (StringUtils.startsWithAny(method.getName(), "get", "select", "find")) {DBContextHolder.read("");}else {DBContextHolder.write();}}}
}

DBTypeEnum.java

数据源枚举,增加和减少数据源修改即可

public enum DBTypeEnum {READ_AW, READ_DC, WRITE;}

DBContextHolder.java

数据源切换类,保持当前线程绑定哪个数据源

package com.test.test.datasource.handler;import com.test.test.datasource.enums.DBTypeEnum;
import org.apache.commons.lang3.StringUtils;import java.util.concurrent.atomic.AtomicInteger;
/*** @Author gmwang* @Description // 数据源切换类* @Date 2019/4/30 9:20* @Param* @return**/
public class DBContextHolder {private static final ThreadLocal<DBTypeEnum> contextHolder = new ThreadLocal<>();private static final AtomicInteger counter = new AtomicInteger(-1);public static void set(DBTypeEnum dbType) {contextHolder.set(dbType);}public static DBTypeEnum get() {return contextHolder.get();}public static void read(String value) {if(StringUtils.isBlank(value)){set(DBTypeEnum.READ_AW);System.out.println("切换到读"+DBTypeEnum.READ_AW.toString());}if (DBTypeEnum.READ_DC.toString().equals(value)){set(DBTypeEnum.READ_DC);System.out.println("切换到读"+DBTypeEnum.READ_DC.toString());}}public static void write() {set(DBTypeEnum.WRITE);System.out.println("切换到写"+DBTypeEnum.WRITE.toString());}
}

MyRoutingDataSource.java

多数据源的路由类

package com.test.test.datasource;import com.test.test.datasource.handler.DBContextHolder;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.lang.Nullable;
/*** @Author gmwang* @Description //多数据源的路由* @Date 2019/4/30 9:38* @Param* @return**/
public class MyRoutingDataSource extends AbstractRoutingDataSource {/*** @Author gmwang* @Description //根据Key获取数据源的信息,上层抽象函数的钩子* @Date 2019/4/30 9:39* @Param []* @return java.lang.Object**/@Nullable@Overrideprotected Object determineCurrentLookupKey() {return DBContextHolder.get();}
}

DbSource

数据源注解,加在serivice实现类上,指定 value,AOP根据注解获取指定的数据源。


package com.test.test.datasource.annotation;import java.lang.annotation.*;@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface DbSource {String value();
}

例如本例中,默认读库是  READ_AW ,如果不加注解默认,读取默认库。如果指定注解 READ_DC,就用指定的数据源。

3.    结果测试

伪代码:

在tes方法中使用查询(不同的库)后插入操作,结果如图所示。

springboot 多数据源 读写分离 AOP方式相关推荐

  1. SpringBoot多数据源切换,AOP实现动态数据源切换

    SpringBoot多数据源切换,AOP实现动态数据源切换 操作数据一般都是在DAO层进行处理,可以选择直接使用JDBC进行编程 或者是使用多个DataSource 然后创建多个SessionFact ...

  2. 分布式数据层中间件详解:如何实现分库分表+动态数据源+读写分离

    分布式数据层中间件: 1.简介: 分布式数据访问层中间件,旨在为供一个通用数据访问层服务,支持MySQL动态数据源.读写分离.分布式唯一主键生成器.分库分表.动态化配置等功能,并且支持从客户端角度对数 ...

  3. MySQL的主从配置+SpringBoot的MySQL读写分离配置

    MySQL的主从复制 点击前往查看MySQL的安装 1.主库操作 vim /etc/my.cnf 添加如下配置 log-bin=mysql-bin #[必须]启用二进制日志 server-id=128 ...

  4. mybatis 配置多数据源 java,SpringBoot+MyBatisPlus配置多数据源读写分离

    首先呢,我们这里使用MySQL的数据库,可以简单配置一下主从备份来实现两个数据库的数据同步 项目的配置目录大概是这样的作为参考 第一步.定义一个枚举类声明当前的数据源类型 package com.yu ...

  5. SpringBoot + MyBatis + MySQL 读写分离实战

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 来源:http://t.cn/AiKuJEB9 1. 引言 读写分 ...

  6. Springboot Mybatis MySQL读写分离及事物配置

    为什么需要读写分离 当项目越来越大和并发越来大的情况下,单个数据库服务器的压力肯定也是越来越大,最终演变成数据库成为性能的瓶颈,而且当数据越来越多时,查询也更加耗费时间,当然数据库数据过大时,可以采用 ...

  7. SpringBoot + Sharding JDBC 读写分离、分库分表

    Sharding-JDBC 最早是当当网内部使用的一款分库分表框架,到2017年的时候才开始对外开源,这几年在大量社区贡献者的不断迭代下,功能也逐渐完善,现已更名为 ShardingSphere,20 ...

  8. java方法嵌套数据源切换_SpringBoot AOP方式实现多数据源切换的方法

    最近在做保证金余额查询优化,在项目启动时候需要把余额全量加载到本地缓存,因为需要全量查询所有骑手的保证金余额,为了不影响主数据库的性能,考虑把这个查询走从库.所以涉及到需要在一个项目中配置多数据源,并 ...

  9. SpringBoot面向切面编程-用AOP方式管理日志

    面向切面编程 认识AOP AOP(Aspect Oriented Program,面向切面编程)把业务功能分为核心.非核心两部分. 核心业务功能 非核心业务功能 用户登录,增加数据,删除数据 性能统计 ...

最新文章

  1. 定义一个类mymath_C++:模板类
  2. hihocoder 1122 : 二分图二•二分图最大匹配之匈牙利算法
  3. django 分页功能
  4. 阿里纳斯Adidas广告词
  5. 研究人员:Intel 的 VISA 漏洞可访问计算机中所有数据
  6. 蓝桥杯 BASIC-19 基础练习 完美的代价
  7. python 策略回测_python策略怎么进行全市场回测-金字塔知识 -程序化交易(CXH99.COM)...
  8. python浮点数转科学计数_python – 将float转换为字符串没有科学记数法和假精度...
  9. VB语言写CRC16校验
  10. YYKit-YYCache分析
  11. 【已解决】node.dll丢失怎么办?计算机丢失node.dll是什么意思?
  12. Quartz定时任务手动触发
  13. 什么是codeLens
  14. MOS管开启过程中VGS的台阶——米勒平台?
  15. 【REVERSE】REVERSE入门
  16. C盘各个文件的简单介绍
  17. 金山文档手机app服务器异常,手机为什么打不开金山文档 打不开金山文档怎么办...
  18. 物体抓取位姿估計算法綜述_NO. 1 物体位姿估计 机器人抓取
  19. 【武忠祥高等数学基础课笔记】反常积分
  20. Python入门学习笔记

热门文章

  1. 前端学习(3081):vue+element今日头条管理-创建页面组件并加载
  2. [html] 说说你对H5的ServiceWorker的理解,它有什么运用场景?
  3. [css] 说出至少十条你理解的css规范
  4. 工作341:uni-表头不显示
  5. [css] 如何修改美化radio、checkbox的默认样式?
  6. 前端学习(2380):调整目录结构
  7. 前端学习(1978)vue之电商管理系统电商系统之为每一行数据提供单独的value
  8. 前端学习(360):svn操作前期连接工作
  9. spring mvc学习(4):第一个spring mvc项目
  10. 第六十六期:软件架构之道的一次感悟