【Spring Boot】Spring Boot之使用AOP实现数据库多数据源自动切换
一、添加maven坐标
<!-- aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- jdbc -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.</version>
</dependency>
二、加入Mybtis配置类(方便测试)
/**
* @author zhangboqing
* @date 2018/8/3
*
* Mybatis配置
*/
@Configuration
@MapperScan(basePackages = {"com.zbq.springbootdemo.dao"}, sqlSessionFactoryRef = "sqlSessionFactory")
//或者直接在Mapper类上面添加注解@Mapper,建议使用上面那种,不然每个mapper加个注解也挺麻烦的
public class MyBatisConfig { }
三、加入多数据源配置
1)修改application.yml添加数据库配置属性
spring:
datasource:
primary:
hikari:
connection-test-query: SELECT FROM DUAL
connection-timeout:
maximum-pool-size:
max-lifetime:
minimum-idle:
validation-timeout:
idle-timeout:
connection-init-sql: SET NAMES utf8mb4
jdbc-url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull
username: root
password:
driver-class-name: com.mysql.jdbc.Driver
secondary:
hikari:
connection-test-query: SELECT FROM DUAL
connection-timeout:
maximum-pool-size:
max-lifetime:
minimum-idle:
validation-timeout:
idle-timeout:
connection-init-sql: SET NAMES utf8mb4
jdbc-url: jdbc:mysql://localhost:3326/test?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull
username: root
password:
driver-class-name: com.mysql.jdbc.Driver
2)添加DataSourceConfig配置类(自定义DataSource数据源)
/**
* @author zhangboqing
* @date 2019-11-17
*/
@Configuration
// 自定义数据源一定要排除SpringBoot自动配置数据源,不然会出现循环引用的问题,The dependencies of some of the beans in the application context form a cycle
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
public class DataSourceConfig { @Bean(name = "primary")
@ConfigurationProperties(prefix = "spring.datasource.primary.hikari")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
} @Bean(name = "secondary")
@ConfigurationProperties(prefix = "spring.datasource.secondary.hikari")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
} /**
* 动态数据源
* 通过AOP+注解实现动态切换
*
* @return
*/
@Primary
@Bean(name = "dynamicDataSource")
public DataSource dataSource() {
DynamicDataSourceRouter dynamicDataSource = new DynamicDataSourceRouter();
// 默认数据源
dynamicDataSource.setDefaultTargetDataSource(primaryDataSource());
// 配置多数据源
Map<Object, Object> dataSourceMap = new HashMap();
dataSourceMap.put("primary", primaryDataSource());
dataSourceMap.put("secondary", secondaryDataSource());
dynamicDataSource.setTargetDataSources(dataSourceMap);
return dynamicDataSource;
} /**
* 配置@Transactional注解事物
*
* @return
*/
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
} }
/**
* @author zhangboqing
* @date 2019-11-17
*/
public class DynamicDataSourceRouter extends AbstractRoutingDataSource{
@Override
protected Object determineCurrentLookupKey() {
return DataSourceNameContextHolder.getDataSourceName();
} @Override
public void setLogWriter(PrintWriter pw) throws SQLException {
super.setLogWriter(pw);
}
}
3)定义 @DataSourceName注解(用于指定sql对应的数据源)
/**
* @author zhangboqing
* @date 2019-11-17
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface DataSourceName { /**
* 指定数据源名称
* @return dataSourceName
*/
String value() default "primary";
}
4)定义DataSourceNameContextHolder类(使用ThreadLocal存放当前线程持有的数据源名称)
/**
* @author zhangboqing
* @date 2019-11-17
*/
@Slf4j
public class DataSourceNameContextHolder { private static final ThreadLocal<String> dataSourceNameContextHolder = new NamedThreadLocal<>("DataSourceContext"); /** 默认数据源名称 */
public static final String DEFAULT_DATASOURCE_NAME = "primary"; public static void setDataSourceName(String dataSourceName) {
log.info("切换到[{}]数据源", dataSourceName);
dataSourceNameContextHolder.set(dataSourceName);
} public static String getDataSourceName() { return dataSourceNameContextHolder.get() != null ? dataSourceNameContextHolder.get() : DEFAULT_DATASOURCE_NAME;
} public static void resetDataSourceName() {
dataSourceNameContextHolder.remove();
}
}
5)定义DynamicDataSourceAspect切面类(通过AOP的方式拦截指定注解实现数据源切换)
/**
* @author zhangboqing
* @date 2019-11-17
*/
@Aspect
@Component
public class DynamicDataSourceAspect { @Before("@annotation(dataSourceName)")
public void beforeSwitchDataSource(DataSourceName dataSourceName){
// 切换数据源
DataSourceNameContextHolder.setDataSourceName(dataSourceName.value());
} @After("@annotation(com.zbq.springbootdemo.config.multidatasource.DataSourceName)")
public void afterSwitchDataSource(){
DataSourceNameContextHolder.resetDataSourceName();
}
}
四、添加测试
1)在Mybtis配置类指定的包下定义一个Dao类并使用注解指定数据源
/**
* @author zhangboqing
* @date 2019-11-21
*/
@Repository
public interface UserDao { @DataSourceName("secondary")
@Select("select * from user order by create_time desc limit 1 ")
public User getNewstOne(); // 默认是primary,所以可以不指定
// @DataSourceName("primary")
@Select("select * from user order by create_time desc limit 1 ")
public User getNewstOne2();
}
2)定义测试类执行
/**
* @author zhangboqing
* @date 2019-11-21
*/
@SpringBootTest
@Slf4j
class UserServiceImplTest { @Autowired
private UserDao userDao; @Test
void getNewestOne() {
User newestOne = userDao.getNewstOne();
User newestOne2 = userDao.getNewstOne2();
log.info(newestOne.toString());
log.info(newestOne2.toString());
}
}
3)执行结果可知多数据源生效,同样的sql查询结果分别来自于两个库
-- ::51.124 INFO --- [ main] c.z.s.service.UserServiceImplTest : User(uid=, phone=, createTime=null, updateTime=null)
-- ::51.124 INFO --- [ main] c.z.s.service.UserServiceImplTest : User(uid=, phone=, createTime=null, updateTime=null)
【Spring Boot】Spring Boot之使用AOP实现数据库多数据源自动切换的更多相关文章
- 四、Spring Boot 多数据源 自动切换
实现案例场景: 某系统除了需要从自己的主要数据库上读取和管理数据外,还有一部分业务涉及到其他多个数据库,要求可以在任何方法上可以灵活指定具体要操作的数据库.为了在开发中以最简单的方法使用,本文基于注解 ...
- Spring Boot 多数据源自动切换
在Spring Boot中使用单数据源的配置很简单,我们简单回忆下:只需要在application.properties进行基本的连接配置,在pom.xml引入基本的依赖即可. 那么多数据源的原理呢? ...
- 43. Spring Boot动态数据源(多数据源自动切换)【从零开始学Spring Boot】
[视频&交流平台] àSpringBoot视频 http://study.163.com/course/introduction.htm?courseId=1004329008&utm ...
- (43). Spring Boot动态数据源(多数据源自动切换)【从零开始学Spring Boot】
在上一篇我们介绍了多数据源,但是我们会发现在实际中我们很少直接获取数据源对象进行操作,我们常用的是jdbcTemplate或者是jpa进行操作数据库.那么这一节我们将要介绍怎么进行多数据源动态切换.添 ...
- Spring标签@Aspect-实现面向方向编程(@Aspect的多数据源自动加载)——SKY
从Spring 2.0开始,可以使用基于schema及@AspectJ的方式来实现AOP.由于@Aspect是基于注解的,因此要求支持注解的5.0版本以上的JDK. 环境要求: 1. mybit ...
- Spring Boot 动态数据源(多数据源自动切换)
本文实现案例场景: 某系统除了需要从自己的主要数据库上读取和管理数据外,还有一部分业务涉及到其他多个数据库,要求可以在任何方法上可以灵活指定具体要操作的数据库. 为了在开发中以最简单的方法使用,本文基 ...
- 22. Spring Boot 动态数据源(多数据源自动切换)
转自:https://blog.csdn.net/catoop/article/details/50575038
- Spring Boot -- Spring AOP原理及简单实现
一.AOP基本概念 什么是AOP,AOP英语全名就是Aspect oriented programming,字面意思就是面向切面编程.面向切面的编程是对面向对象编程的补充,面向对象的编程核心模块是类, ...
- 【Spring】关于Boot应用中集成Spring Security你必须了解的那些事
Spring Security Spring Security是Spring社区的一个顶级项目,也是Spring Boot官方推荐使用的Security框架.除了常规的Authentication和A ...
随机推荐
- 遵循PEP8风格
遵循PEP8风格 在编写python代码的时候我们应该采用统一的风格来编写代码,可以使代码更加容易读懂.采用和其他程序员统一的编码风格来写代码,也可以使项目更利于多人协作开发.即便代码只有你自己能看懂 ...
- ORB-SLAM2初步(跟踪模块)
一.跟踪模块简介 在ORB-SLAM或其他SLAM系统中,跟踪的主要任务是根据相机或视频输入的图像帧实时输出相机位姿.在ORB-SLAM中,跟踪模块的主要任务是实时输出相机位姿和筛选关键帧,完成一个没 ...
- [LeetCode] 215. Kth Largest Element in an Array 数组中第k大的数字
Find the kth largest element in an unsorted array. Note that it is the kth largest element in the so ...
- oracle--10.2.0.3升级到11.2.0.4
一,环境 01,待升级的系统 升级仅支持10.2.0.2版本之后的系统,如果不是,请把10G升级至高版本! 本次实验环境10.2.0.3 02,挂载11G系统 03,升级须知 1) 做好备份 二,DB ...
- 【转】python 调用super()初始化报错“TypeError: super() takes at least 1 argument”
一.实验环境 1.Windows7x64_SP1 2.Anaconda2.5.0 + python2.7(anaconda集成,不需单独安装) 二.实验步骤 2.1 在python中有如下代码: cl ...
- ab小工具的Failed requests多的问题
ab小工具的Failed requests多的问题 这个是PHP返回的length不一致造成的 是ab的bug 所以不用理会.. 测试并发写100就行了 一般100没错误
- 明解C语言 入门篇 第五章答案
练习5-1 /* 依次把1.2.3.4.5 赋值给数组的每个元素并显示(使用for语句) */ #include <stdio.h> int main(void) { int i; ]; ...
- CentOS7 SUDO 笔记--没配置sudoer,为什么有的账号能用sudo命令,有的不能用
原来: 一.安装linux 创建的用户(管理员打钩)默认在 wheel组里. 1. 使用 cat /etc/passwd 查看用户所在组.中间那个数字是 groupid 不太好看 2.使用 cat / ...
- Java学习清单
转自: csdn/zuochao_2013/article/details/76795164 · Java基础部分 *Java基础才是重中之重,只有基础打牢了,学习各种框架才能游刃有余. 1, ...
- 【More Effective C++ 条款1】仔细区别pointers和references
1)操作符的差别 指针使用"*"和"->"操作符,而引用使用"."操作符 2)初始化的差别 有空指针,但是没有空引用,和const对象 ...