springboot+mybatis多数据源配置
目录
1、前言
2、多数据源配置
2.1 AbstractRoutingDataSource
2.2、首先maven依赖
2.3 数据源配置
2.4 mybatis配置
2.5 设置数据源的路由key 以及 查找数据源
3、切面拦截
3.1 定义切面注解
3.2 切面实现类
4、测试Controller
1、前言
在最近的项目开发中,有需要把多个服务整合,涉及到了多个数据库访问。所以记录一下多数据源的配置。在我自己的项目中,JDK使用的是1.8版本,SpringBoot是1.5.21版本。
2、多数据源配置
2.1 AbstractRoutingDataSource
动态数据源的配置是基于key路由到特定的数据源,由路由key与目标数据源产生映射关系。springboot提供了一个抽象类AbstractRoutingDataSource来实现,我们只需要实现determineCurrentLookupKey方法即可。
2.2、首先maven依赖
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>1.5.21.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>${fastjson.version}</version></dependency><!-- spring集成热部署 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional></dependency><!-- spring集成配置文件的操作组件 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><!-- druid连接池监控 --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>${druid.version}</version></dependency><!-- jdbc连接驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.0.1</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId></dependency><!--reflectasm使用字节码生成的方式实现了更为高效的反射机制 --><dependency><groupId>com.esotericsoftware</groupId><artifactId>reflectasm</artifactId><version>1.11.9</version></dependency><!-- redis使用 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>commons-lang</groupId><artifactId>commons-lang</artifactId><version>2.6</version></dependency><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>1.8</source><target>1.8</target></configuration></plugin></plugins><defaultGoal>compile</defaultGoal></build>
2.3 数据源配置
application.yml配置
spring:datasource:user:driverClassName: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://192.168.1.15:3306/test_user?serverTimezone=Asia/Shanghai&allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghaiusername: rootpassword: rootpatient:driverClassName: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://192.168.1.15:3306/test_patient?serverTimezone=Asia/Shanghai&allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghaiusername: rootpassword: rootdoctor:driverClassName: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://192.168.1.15:3306/test_doctor?serverTimezone=Asia/Shanghai&allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghaiusername: rootpassword: rootdruid:initialSize: 5minIdle: 5maxActive: 20maxWait: 60000timeBetweenEvictionRunsMillis: 60000minEvictableIdleTimeMillis: 300000validationQuery: SELECT 1 FROM DUALtestWhileIdle: truetestOnBorrow: falsetestOnReturn: false poolPreparedStatements: truemaxPoolPreparedStatementPerConnectionSize: 20filters: stat,log4jconnectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
多数据源配置类
import java.util.HashMap;
import java.util.Map;import javax.sql.DataSource;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import com.alibaba.druid.pool.DruidDataSourceFactory;/*** 动态数据源配置* @author LongBJ**/
@Configuration
@MapperScan("com.kingseok.hospital.business.*.dao")
public class DynamicDataSourceConfig implements EnvironmentAware {/*** user数据源* * @return*/@Bean@ConfigurationProperties(prefix = "spring.datasource.user")public DataSource userDataSource() {//DruidDataSourceFactory.createDataSource(properties);return DataSourceBuilder.create().build();}/*** patient数据源* @return*/@Bean@ConfigurationProperties(prefix = "spring.datasource.patient")public DataSource patientDataSource() {return DataSourceBuilder.create().build();}/*** doctor数据源* @return*/@Bean@ConfigurationProperties(prefix = "spring.datasource.doctor")public DataSource doctorDataSource() {return DataSourceBuilder.create().build();}/*** 配置动态数据源* * @return*/@Primary // 此处指定动态数据源为primary@Bean(name = "dynamicDataSource")public DataSource dynamicDataSource(@Qualifier("userDataSource") DataSource userDataSource,@Qualifier("patientDataSource") DataSource patientDataSource, @Qualifier("doctorDataSource") DataSource doctorDataSource) {Map<Object, Object> dataSourceMap = new HashMap<>(3);dataSourceMap.put(DataSourceEnum.DS_USER.getValue(), userDataSource);dataSourceMap.put(DataSourceEnum.DS_PATIENT.getValue(), patientDataSource);dataSourceMap.put(DataSourceEnum.DS_DOCTOR.getValue(), doctorDataSource);DynamicDataSource dynamicDataSource = new DynamicDataSource();dynamicDataSource.setTargetDataSources(dataSourceMap);dynamicDataSource.setDefaultTargetDataSource(userDataSource); // 指定默认的数据源return dynamicDataSource;}@Autowiredprivate Environment env;@Overridepublic void setEnvironment(Environment environment) {this.env = environment;}}
此处配置了4个数据源,分别是user、patient、doctor,还有一个路由数据源。前面3个是为了生成第4个数据源,这个是动态数据源的关键,后面的数据源切换也是从这里选择。
2.4 mybatis配置
import javax.annotation.Resource;
import javax.sql.DataSource;import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
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;/**
** Mybatis配置
**/
@Configuration
@EnableTransactionManagement
public class MyBatisConfig {@Resource(name = "dynamicDataSource")private DataSource dynamicDataSource;/*** sqlSessionFactory* @return* @throws Exception*/@Beanpublic SqlSessionFactory sqlSessionFactory() throws Exception {SqlSessionFactoryBean factory = new SqlSessionFactoryBean();factory.setDataSource(dynamicDataSource);PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();factory.setMapperLocations(resolver.getResources("classpath:mapper/**/*.xml"));return factory.getObject();}@Beanpublic SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {return new SqlSessionTemplate(sqlSessionFactory);}/*** 事务管理器* @return*/@Beanpublic PlatformTransactionManager platformTransactionManager() {return new DataSourceTransactionManager(dynamicDataSource); }
}
由于我们是多数据源,所以我们要为事务管理器和mybatis手动指定明确的数据源。
2.5 设置数据源的路由key 以及 查找数据源
首先我们定义一个数据源枚举(常量类也行),如下:
public enum DataSourceEnum {DS_USER("DS_USER", "user数据源"),DS_PATIENT("DS_PATIENT", "patient数据源"),DS_DOCTOR("DS_DOCTOR","doctor数据源"),DS_KUAIMA_MEDICAL("medical", "medical数据源"),DS_PDD_IM("pdd_im_data", "im数据源");private String value;private String display;DataSourceEnum(String value, String display) {this.value = value;this.display = display;}public String getValue() {return value;}public void setValue(String value) {this.value = value;}public String getDisplay() {return display;}public void setDisplay(String display) {this.display = display;}
}
接下来,我们利用ThreadLocal将数据源设置到每个请求的线程上下文当中,代码如下:
/**
** 动态数据源线程上下文操作类
**/
public class DynamicDataSourceContextHolder {private final static ThreadLocal<String> DATASOURCE_CONTEXT_KEY_HOLDER = new ThreadLocal<>();/*** 设置/切换数据源*/public static void setContextKey(String key){DATASOURCE_CONTEXT_KEY_HOLDER.set(key);}/*** 获取数据源名称*/public static String getContextKey(){String key = DATASOURCE_CONTEXT_KEY_HOLDER.get();return key == null ? DataSourceEnum.DS_USER.getValue() : key;}/*** 删除当前数据源名称*/public static void removeContextKey(){DATASOURCE_CONTEXT_KEY_HOLDER.remove();}}
前面说过动态数据源选择,springboot提供的抽象类AbstractRoutingDataSource提供了一个方法,我们实现这个方法即可,所以我们创建一个动态数据源选择类,然后继承AbstractRoutingDataSource,如下:
/*** 动态数据源选择* @author LongBJ**/
public class DynamicDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {// 前面将用户请求的数据源路由key set到threadLocal, 此处从ThreadLocal获取出来,// 拿到key之后,找到对应的数据源(key就是DynamicDataSourceConfig类中配置的key)return DynamicDataSourceContextHolder.getContextKey();}}
至此,多数据源便已完全配置,接下来就是如何识别用户的访问哪一个数据源,我自己的项目是通过AOP切面来实现的。
3、切面拦截
3.1 定义切面注解
定义一个切面注解用于访问的类或者方法上,如下:
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.ElementType.METHOD;import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;import com.kingseok.hospital.common.configuration.DataSourceEnum;@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ TYPE, METHOD }) // TYPE可用户类上,METHOD用于请求方法上
public @interface SqlDynamicDS {DataSourceEnum value() default DataSourceEnum.DS_USER;}
3.2 切面实现类
有了切面注解,还需要实现切面拦截,实现非常简单,只需要定义一个类,在类上使用@Aspect和@Component即可实现。
/*** mysql动态数据源切面* @author LongBJ**/
@Aspect
@Component
@Order(value = Ordered.HIGHEST_PRECEDENCE)
public class DynamicDatasourceAspect {private final static Logger _logger = LoggerFactory.getLogger(DynamicDatasourceAspect.class);@Pointcut("@within(com.kingseok.hospital.common.annotation.SqlDynamicDS)")public void dataSourcePointCut() {}@Pointcut("@annotation(com.kingseok.hospital.common.annotation.SqlDynamicDS)")public void dataSourcePointCut2() {}@Around("dataSourcePointCut() || dataSourcePointCut2()")public Object getSqlDynamicDS(ProceedingJoinPoint joinPoint) throws Throwable {DataSourceEnum ds = getDSAnnocation(joinPoint).value();_logger.info("DynamicDatasourceAspect : getSqlDynamicDS -> ds_key={}", ds.getValue());DynamicDataSourceContextHolder.setContextKey(ds.getValue());try{return joinPoint.proceed();}finally {DynamicDataSourceContextHolder.removeContextKey();_logger.info("DynamicDatasourceAspect : getSqlDynamicDS -> ThreadLocal已删除ds_key={}", ds.getValue());} }private SqlDynamicDS getDSAnnocation(ProceedingJoinPoint joinPoint) {Class<?> targetClazz = joinPoint.getTarget().getClass();SqlDynamicDS ds = targetClazz.getAnnotation(SqlDynamicDS.class);// 先判断类的注解,再判断方法注解if(Objects.nonNull(ds)) {return ds;}MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();return methodSignature.getMethod().getAnnotation(SqlDynamicDS.class);}}
这里需要特别注意,我定义了2个切面。为什么定义2个,有眼尖的同学会发现@Pointcut("@within(com.kingseok.hospital.common.annotation.SqlDynamicDS)")和@Pointcut("@annotation(com.kingseok.hospital.common.annotation.SqlDynamicDS)")仅仅是@within和@annotation不同,其实@within:用于匹配所持有的指定直接类型内的所有方法;而@annotation:用于配置当前执行方法持有指定注解的方法。参考如下:
4、测试Controller
import com.cjs.example.entity.Member;
import com.cjs.example.service.MemberService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;@RunWith(SpringRunner.class)
@SpringBootTest
@SqlDynamicDS(value = DataSourceEnum.DS_USER) // 作用于类上,则此类所有方法请求都将路由到数据源DataSourceEnum.DS_USER上 类的优先级高于方法
public class DatasourceDemoApplicationTests {@Autowiredprivate MemberService memberService;@Testpublic void testWrite() {Member member = new Member();member.setName("zhangsan");memberService.insert(member);}@Test@SqlDynamicDS(value = DataSourceEnum.DS_USER) // 作用于方法上public void testReadFromMaster() {memberService.getUser("zhangsan");}}
至此,这个多数据源配置已完成
springboot+mybatis多数据源配置相关推荐
- SpringBoot mybatis多数据源配置,记录下我磕磕碰碰的三个月找工作经历
*/ public class DynamicDataSource extends AbstractRoutingDataSource { /** 取得当前使用哪个数据源 @return */ @Ov ...
- SpringBoot Mybatis多数据源配置
参考资料: Spring Boot 2.x基础教程:MyBatis的多数据源配置 目录 一. 配置文件 二. 多数据源配置类 三. 多数据源Mybatis配置 3.1 primary数据源配置 3.2 ...
- Springboot整合Mybatis多数据源配置
话不多说,直接进入正题.源码地址:https://github.com/SuriYesl/template.git 目录 一.数据库配置文件 二.配置类 主数据源配置类: 次数据源配置类: 三.项目结 ...
- 探讨 | SpringBoot + MyBatis 多数据源事物问题
这是小小本周的第二篇,本篇将会着重讲解关于SpringBoot + MyBatis 多数据源的事物的问题. 多数据源 此处模拟创建订单和扣减库存.先创建订单表和库存表 CREATE TABLE `t_ ...
- springmvc+mybatis多数据源配置,AOP注解动态切换数据源
springmvc与springboot没多大区别,springboot一个jar包配置几乎包含了所有springmvc,也不需要繁琐的xml配置,springmvc需要配置多种jar包,需要繁琐的x ...
- MyBatis多数据源配置(读写分离)
MyBatis多数据源配置(读写分离) 首先说明,本文的配置使用的最直接的方式,实际用起来可能会很麻烦. 实际应用中可能存在多种结合的情况,你可以理解本文的含义,不要死板的使用. 多数据源的可能情况 ...
- springboot+dynamic多数据源配置
springboot+dynamic多数据源配置 配置 使用 springboot+dynamic多数据源配置 来源:https://mp.baomidou.com/guide/dynamic-dat ...
- springboot中druid数据源配置无效的问题和jar包找不到问题
springboot中druid数据源配置无效的问题 阿里云的仓库 链接: 阿里云仓库. 自己在springboot项目中,引入druid的依赖,希望引入druid数据源. 但是idea中,虽然在这个 ...
- spring mybatis 多数据源配置 jeesite 多数据源配置
spring mybatis 多数据源配置 jeesite 多数据源配置 一.情景描述 在系统数据达到一定的访问量时,遇到单个数据库瓶颈,所以需要扩展数据库,启用第二个数据源资源,项目架构变成 一个服 ...
最新文章
- 使用OpenCV自动去除背景色
- 为什么美团打车、滴滴外卖必败?君智谢伟山揭秘了背后的竞争战略逻辑
- Spark Streaming介绍,DStream,DStream相关操作(来自学习资料)
- For each...in / For...in / For...of 的解释与例子
- 《ASP.NET Core 与 RESTful API 开发实战》-- (第10章)-- 读书笔记
- win10 hyper-v 虚拟机ping不通宿主机问题
- Linux C 指针练习
- string字符串的高级用法
- 数据结构与算法java_数据结构与算法(Java语言版).pdf
- 【SQL】基础增删改查
- 基于AI的恶意软件分类技术(4)
- UVa 10608 - Friends(并查集)
- Spring In Action 4 学习笔记(一)Spring概览
- 剖析top命令显示的VIRT RES SHR值
- 乐高机器人走进图书馆活动方案_欢迎参加“乐高机器人创意搭建赛”活动
- android 地球坐标 火星坐标系,Android LBS地图开发:地球地理GPS坐标系经纬度偏移偏差...
- Gateway 网关
- ker矩阵是什么意思_重做第一次作业(矩阵)
- 手动安装Python库或安装第三方库的方法
- 利用OCR文字识别+百度算法搜索,玩转冲顶大会、百万英雄、芝士超人等答题赢奖金游戏...
热门文章
- 使用四种框架分别实现百万websocket常连接的服务器{转}
- C#跨线程调用窗体控件的问题
- 同花顺2020春招一面二面在线编程题(java开发)
- 84-MongoDB高级介绍
- ASP.NET购物车(源码下载)
- 远传融创-啥是比特?哈利波特?
- 嵌入式Linux学习笔记(1-2)——vi编辑器
- 太原市高二会考计算机试题及答案,太原市第二十一中学校2019-2020学年高二会考模拟生物试卷...
- 【python】DHCP_Discover数据包构造
- 面向对象开发方法,Coad方法、Booch方法和OMT方法及UML