从mybatis拦截器维度处理读写分离的多数据源问题
多数据源的应用场景比较多,前面已经分享了几种,今天我再通过拦截器的角度分享一次,这样也更贴近实战,更准确,今天主要验证拦截器,比如读写分离的场景,在拦截器接口判断此条接口是查询还是修改,如果是查询赋值读库的数据源,如果是修改就赋值写库的数据库,这样也能精准的处理读写分离的业务问题。废话少数,下面咱们开始:
1、pom文件引入相关jar:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.1</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--Druid连接池--><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.8</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>
2、数据库配置,暂定两个固定数据库:
spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcemaster-db:url: jdbc:mysql://127.0.0.1:3306/master-db?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=falseusername: rootpassword: rootinitial-size: 1min-idle: 1max-active: 20test-on-borrow: truedriver-class-name: com.mysql.cj.jdbc.Driverslave-db:url: jdbc:mysql://127.0.0.1:3306/slave-db?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=falseusername: rootpassword: rootinitial-size: 1min-idle: 1max-active: 20test-on-borrow: truedriver-class-name: com.mysql.cj.jdbc.Driver
3、数据库初始化配置类
package com.nandao.dynamic.datasource.config;import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.nandao.dynamic.datasource.DynamicDataSource;
import com.nandao.dynamic.datasource.plugin.DynamicDataSourcePlugin;
import org.apache.ibatis.plugin.Interceptor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;import javax.sql.DataSource;/**** @Author nandao*/
@Configuration
public class DataSourceConfig {@Bean@ConfigurationProperties(prefix = "spring.datasource.master-db")public DataSource dataSource1() {// 底层会自动拿到spring.datasource中的配置, 创建一个DruidDataSourcereturn DruidDataSourceBuilder.create().build();}@Bean@ConfigurationProperties(prefix = "spring.datasource.slave-db")public DataSource dataSource2() {// 底层会自动拿到spring.datasource中的配置, 创建一个DruidDataSourcereturn DruidDataSourceBuilder.create().build();}@Bean//或者在DynamicDataSourcePlugin类上添加@Component注解public Interceptor dynamicDataSourcePlugin(){return new DynamicDataSourcePlugin();}@Beanpublic DataSourceTransactionManager transactionManager1(DynamicDataSource dataSource){DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();dataSourceTransactionManager.setDataSource(dataSource);return dataSourceTransactionManager;}@Beanpublic DataSourceTransactionManager transactionManager2(DynamicDataSource dataSource){DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();dataSourceTransactionManager.setDataSource(dataSource);return dataSourceTransactionManager;}
}
4、定义多数据源的实现类
package com.nandao.dynamic.datasource;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component;import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;/**** @Author nandao*/
@Component
@Primary // 将该Bean设置为主要注入Bean
public class DynamicDataSource extends AbstractRoutingDataSource {// 当前使用的数据源标识public static ThreadLocal<String> name = new ThreadLocal<>();// 写@AutowiredDataSource dataSource1;// 读@AutowiredDataSource dataSource2;// 返回当前数据源标识@Overrideprotected Object determineCurrentLookupKey() {return name.get();}@Overridepublic void afterPropertiesSet() {// 为targetDataSources初始化所有数据源Map<Object, Object> targetDataSources=new HashMap<>();targetDataSources.put("W",dataSource1);targetDataSources.put("R",dataSource2);super.setTargetDataSources(targetDataSources);// 为defaultTargetDataSource 设置默认的数据源super.setDefaultTargetDataSource(dataSource1);super.afterPropertiesSet();}
}
5、自定义注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/**** @Author nandao*/
@Target({ElementType.METHOD,ElementType.TYPE})
// 方式
@Retention(RetentionPolicy.RUNTIME)
public @interface WR {String value() default "W";
}
6、切面实现拦截数据源
package com.nandao.dynamic.datasource.aspect;import com.nandao.dynamic.datasource.DynamicDataSource;
import com.nandao.dynamic.datasource.annotation.WR;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;/**** @Author nandao*/
@Component
@Aspect
public class DynamicDataSourceAspect implements Ordered {// 前置@Before("within(com.nandao.dynamic.datasource.service.impl.*) && @annotation(wr)")public void before(JoinPoint point, WR wr){String name = wr.value();DynamicDataSource.name.set(name);System.out.println(name);}@Overridepublic int getOrder() {return -1;}
}
7、mybatis的拦截器处理数据源
package com.nandao.dynamic.datasource.plugin;import com.nandao.dynamic.datasource.DynamicDataSource;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.stereotype.Component;import java.util.Properties;/**** @Author nandao*/
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class,ResultHandler.class})})
@Component // 或者在配置类里添加一个@Bean对象
public class DynamicDataSourcePlugin implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 拿到当前方法(update、query)所有参数Object[] objects = invocation.getArgs();// MappedStatement 封装CRUD所有的元素和SQLMappedStatement ms = (MappedStatement) objects[0];// 读方法if (ms.getSqlCommandType().equals(SqlCommandType.SELECT)) {DynamicDataSource.name.set("R");} else {// 写方法DynamicDataSource.name.set("W");}return invocation.proceed();}@Overridepublic Object plugin(Object target) {if (target instanceof Executor) {return Plugin.wrap(target, this);} else {return target;}}@Overridepublic void setProperties(Properties properties) {}
}
8、业务伪代码参考
控制层:
package com.nandao.dynamic.datasource.controller;import com.nandao.dynamic.datasource.entity.Frend;
import com.nandao.dynamic.datasource.service.FrendService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.List;/**** @Author nandao*/
@RestController
@RequestMapping("frend")
@Slf4j
public class FrendController {@Autowiredprivate FrendService frendService;@GetMapping(value = "select")public List<Frend> select(){return frendService.list();}@GetMapping(value = "insert")public void in(){Frend frend = new Frend();frend.setName("南道");frendService.save(frend);}
}
业务层:
package com.nandao.dynamic.datasource.service.impl;import com.nandao.dynamic.datasource.annotation.WR;
import com.nandao.dynamic.datasource.mapper.FrendMapper;
import com.nandao.dynamic.datasource.entity.Frend;
import com.nandao.dynamic.datasource.service.FrendService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;/**** @Author nandao*/
@Service
public class FrendImplService implements FrendService {@AutowiredFrendMapper frendMapper;@Override@WR("R") //读库public List<Frend> list() {return frendMapper.list();}@Override@WR("W") // 写库public void save(Frend frend) {frendMapper.save(frend);}
}
mapper层:
package com.nandao.dynamic.datasource.mapper;import com.nandao.dynamic.datasource.entity.Frend;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;import java.util.List;/*** @Auther nandao*/
public interface FrendMapper {@Select("SELECT * FROM Frend")List<Frend> list();@Insert("INSERT INTO frend(`name`) VALUES (#{name})")void save(Frend frend);
}
postman测试完验证多数据源无误!在实际使用过程,mybtais 拦截器和AOP切面方式二选一。
到此,今天的多数据源分享完毕,多数据源分享暂时告一段落,下期我们分析mybatis的源码,敬请期待!
从mybatis拦截器维度处理读写分离的多数据源问题相关推荐
- MySQL拦截器获取xml id_关于mybatis拦截器,有谁知道怎么对结果集进行拦截,将指定字段查询结果进行格式化...
用MyBatis结果集拦截器做过这样一个需求: 由于项目需求经常变动,项目MySQL数据库都是存放JSON字符串,例如:用户的基本信息随着版本升级可能会有变动 数据表 CREATE TABLE `ac ...
- list mybatis 接收 类型_基于mybatis拦截器实现的一款简易影子表自动切换插件
近期因工作需要,小编基于mybatis拦截器开发了一款简易影子表自动切换插件,可以根据配置实现动态修改表名,即将对原source table表的操作自动切换到对target table表的操作.该插件 ...
- 面试官:你能说说MyBatis拦截器原理吗?
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:Format cnblogs.com/fangjian042 ...
- 犯罪心理解读Mybatis拦截器
原文链接:"犯罪心理"解读Mybatis拦截器 Mybatis拦截器执行过程解析 文章写过之后,我觉得 "Mybatis 拦截器案件"背后一定还隐藏着某种设计动 ...
- MyBatis拦截器原理探究MyBatis拦截器原理探究
MyBatis拦截器介绍 MyBatis提供了一种插件(plugin)的功能,虽然叫做插件,但其实这是拦截器功能.那么拦截器拦截MyBatis中的哪些内容呢? 我们进入官网看一看: MyBatis拦截 ...
- MyBatis拦截器有哪些以及分析
MyBatis提供了一种插件(plugin)的功能,虽然叫做插件,但其实这是拦截器功能.那么拦截器拦截MyBatis中的哪些内容呢? 我们进入官网看一看: MyBatis 允许你在已映射语句执行过程中 ...
- Mybatis拦截器 mysql load data local 内存流处理
Mybatis 拦截器不做解释了,用过的基本都知道,这里用load data local主要是应对大批量数据的处理,提高性能,也支持事务回滚,且不影响其他的DML操作,当然这个操作不要涉及到当前所lo ...
- MyBatis拦截器原理探究
MyBatis拦截器介绍 MyBatis提供了一种插件(plugin)的功能,虽然叫做插件,但其实这是拦截器功能.那么拦截器拦截MyBatis中的哪些内容呢? 我们进入官网看一看: MyBatis 允 ...
- insert into select 主键自增_springboot2结合mybatis拦截器实现主键自动生成
点击上方蓝字关注我们 1 01 前言 前阵子和朋友聊天,他说他们项目有个需求,要实现主键自动生成,不想每次新增的时候,都手动设置主键.于是我就问他,那你们数据库表设置主键自动递增不就得了.他的回答是他 ...
最新文章
- ES6中的异步对象Promise
- python数据分析的主要流程-Python数据分析全流程实操指南
- python综合学习一之多线程
- tom大叔blog--------深入理解javascript系列-----------笔记
- postgres 禁止远程登录_Windows 7禁止可移动存储设备写入数据,只有想不到,没有做不到...
- 狗窝里的小日子- 4 ...
- 交换最小值和最大值 (15 分)
- echarts树图节点垂直间距_铝模板的安装、拆除、节点、禁止做法详解
- memcached构建高性能web应用
- 跟我一起来用C++写Web服务器吧
- 打印当前html页面 有背景,word打印时页面背景颜色怎么去掉
- IP-Guard十六个模块功能详解
- 5G手机占比逼近四成,华为和小米将加速5G普及
- 解决msvcr120.dll文件丢失问题(搞了半天,简直奔溃,最后完美解决)
- 如何利用迅雷来下载百度云文件?(此方法只适用于大文件需要启动百度网盘的客户端这种情况)...
- 计算机配置中什么表示硬盘,电脑硬盘的分类介绍 硬盘中的Master和Slave代表什么意思...
- 《江山美人》:程小东的利落与编剧群的故步自封
- 掘金 AMA:听闲鱼客户端架构师--邬吉风聊 Flutter 和移动端开发那些事
- OA系统实现(请假审批,mybatis)-2
- S2SH水费管理系统-JAVA【数据库设计、源码、开题报告】