springboot 双数据源+aop动态切换
# springboot-double-data
springboot-double-data
应用场景
项目需要同时连接两个不同的数据库A, B,并且它们都为主从架构,一台写库,多台读库。
多数据源
首先要将spring boot自带的DataSourceAutoConfiguration禁掉,因为它会读取application.properties文件的spring.datasource.*属性并自动配置单数据源。在@SpringBootApplication注解中添加exclude属性即可:
//一般你启动springboot项目,都会写一个有@SpringBootApplication注解的类
//你在这个注解中添加exclude={DataSourceAutoConfiguration.class,HibernateJpaAutoConfiguration.class}
//即可无数据库运行
//@SpringBootApplication//(exclude={DataSourceAutoConfiguration.class,HibernateJpaAutoConfiguration.class})
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableSwagger2
@EnableDiscoveryClient
@ServletComponentScan
@ComponentScan("app")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
然后在application.properties中配置多数据源连接信息:
#mysql1
spring.datasource.db1.url=jdbc:mysql://10.96.140.136:3306/activiti?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.db1.username=ipdata
spring.datasource.db1.password=open2013
spring.datasource.db1.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.db1.max-idle=10
spring.datasource.db1.max-wait=10000
spring.datasource.db1.min-idle=5
spring.datasource.db1.initial-size=5
#mysql2
spring.datasource.db2.url=jdbc:mysql://10.96.140.136:3306/cmdb_resource_module?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.db2.username=ipdata
spring.datasource.db2.password=open2013
spring.datasource.db2.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.db2.max-idle=10
spring.datasource.db2.max-wait=10000
spring.datasource.db2.min-idle=5
spring.datasource.db2.initial-size=5
由于我们禁掉了自动数据源配置,因些下一步就需要手动将这些数据源创建出来:
package app.configuration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* 多数据源配置类
* Created by pure on 2018-05-06.
*/
@Configuration
public class DataSourceConfig {
//由于我们禁掉了自动数据源配置,因些下一步就需要手动将这些数据源创建出来:
//数据源1
@Bean(name = "datasource1")
@Primary
@ConfigurationProperties(prefix = "spring.datasource.db1") // application.properteis中对应属性的前缀
public DataSource dataSource1() {
return DataSourceBuilder.create().build();
}
//数据源2
@Bean(name = "datasource2")
@ConfigurationProperties(prefix = "spring.datasource.db2") // application.properteis中对应属性的前缀
public DataSource dataSource2() {
return DataSourceBuilder.create().build();
}
}
接下来需要配置两个mybatis的SqlSessionFactory分别使用不同的数据源:
package app.configuration;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan(basePackages = {"app.mapper.a"}, sqlSessionFactoryRef = "sqlSessionFactory1")
public class MybatisDbAConfig {
@Autowired
@Qualifier("datasource1")
private DataSource ds1;
@Bean
public SqlSessionFactory sqlSessionFactory1() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(ds1); // 使用ds1数据源, 连接ds1库
return factoryBean.getObject();
}
@Bean
public SqlSessionTemplate sqlSessionTemplate1() throws Exception {
SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactory1()); // 使用上面配置的Factory
return template;
}
}
经过上面的配置后,app.mapper.b下的Mapper接口,都会使用titan数据源。同理可配第二个SqlSessionFactory:
package app.configuration;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan(basePackages = {"app.mapper.b"}, sqlSessionFactoryRef = "sqlSessionFactory2")
public class MybatisDbBConfig {
@Autowired
@Qualifier("datasource2")
private DataSource ds2;
@Bean
public SqlSessionFactory sqlSessionFactory2() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(ds2); // 使用ds2数据源, 连接ds2库
return factoryBean.getObject();
}
@Bean
public SqlSessionTemplate sqlSessionTemplate2() throws Exception {
SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactory2()); // 使用上面配置的Factory
return template;
}
}
完成这些配置后,假设有2个Mapper app.mapper.a和app.mapper.b,使用前者时会自动连接ds1库,后者连接ds2库。
动态数据源
使用动态数据源的初衷,是能在应用层做到读写分离,即在程序代码中控制不同的查询方法去连接不同的库。除了这种方法以外,数据库中间件也是个不错的选择,它的优点是数据库集群对应用来说只暴露为单库,不需要切换数据源的代码逻辑。
我们通过自定义注解 + AOP的方式实现数据源动态切换。
首先定义一个DataSourceContextHolder, 用于保存当前线程使用的数据源名:
package app.configuration;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class DataSourceContextHolder {
private static Logger log = LogManager.getLogger(DataSourceContextHolder.class);
/**
* 默认数据源
*/
public static final String DEFAULT_DS = "db1";
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
// 设置数据源名
public static void setDB(String dbType) {
System.out.println("切换到{}数据源"+ dbType);
contextHolder.set(dbType);
}
// 获取数据源名
public static String getDB() {
return (contextHolder.get());
}
// 清除数据源名
public static void clearDB() {
contextHolder.remove();
}
}
然后自定义一个javax.sql.DataSource接口的实现,这里只需要继承Spring为我们预先实现好的父类AbstractRoutingDataSource即可:
package app.configuration;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource{
private static final Logger log = LogManager.getLogger(DynamicDataSource.class);
@Override
protected Object determineCurrentLookupKey() {
System.out.println("数据源为{}"+DataSourceContextHolder.getDB());
return DataSourceContextHolder.getDB();
}
}
创建动态数据源:
/**
* 动态数据源: 通过AOP在不同数据源之间动态切换
* @return
*/
@Bean(name = "datasource1")
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
// 默认数据源
dynamicDataSource.setDefaultTargetDataSource(dataSource1());
// 配置多数据源
Map<Object, Object> dsMap = new HashMap<>(2);
dsMap.put("datasource1", dataSource1());
dsMap.put("datasource2", dataSource2());
dynamicDataSource.setTargetDataSources(dsMap);
return dynamicDataSource;
}
自定义注释@DS用于在编码时指定方法使用哪个数据源:
package app.configuration;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注释@DS用于在编码时指定方法使用哪个数据源:
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DS {
String value() default "datasource1";
}
编写AOP切面,实现切换逻辑:
package app.configuration;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
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;
/**
* 自定义注解 + AOP的方式实现数据源动态切换。
* Created by pure on 2018-05-06.
*/
@Aspect
@Component
public class DynamicDataSourceAspect {
@Before("@annotation(DS)")
public void beforeSwitchDS(JoinPoint point){
//获得当前访问的class
Class<?> className = point.getTarget().getClass();
//获得访问的方法名
String methodName = point.getSignature().getName();
//得到方法的参数的类型
Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();
String dataSource = DataSourceContextHolder.DEFAULT_DS;
try {
// 得到访问的方法对象
Method method = className.getMethod(methodName, argClass);
// 判断是否存在@DS注解
if (method.isAnnotationPresent(DS.class)) {
DS annotation = method.getAnnotation(DS.class);
// 取出注解中的数据源名
dataSource = annotation.value();
}
} catch (Exception e) {
e.printStackTrace();
}
// 切换数据源
DataSourceContextHolder.setDB(dataSource);
}
@After("@annotation(DS)")
public void afterSwitchDS(JoinPoint point){
DataSourceContextHolder.clearDB();
}
}
完成上述配置后,在先前SqlSessionFactory配置中指定使用DynamicDataSource就可以在Service中愉快的切换数据源了:
@DS("datasource2")
public List<PieEcharts> findVmPie() {
List<PieEcharts> findVmPie = echartsMapper.findVmPie();
return findVmPie;
}
springboot 双数据源+aop动态切换的更多相关文章
- 基于springboot通过注解AOP动态切换druid多数据源--mybatis
控制于接口之上: 开始:demo地址 在lsr-core-base中 自定义注解: /** * @Description: 数据源切换注解 * @Package: lsr-microservice ...
- Springboot+Mybatis+Pagehelper+Aop动态配置Oracle、Mysql数据源
本文链接:https://blog.csdn.net/wjy511295494/article/details/78825890 Springboot+Mybatis+Pagehelper+Aop ...
- Spring-Boot 多数据源配置+动态数据源切换+多数据源事物配置实现主从数据库存储分离
一.基础介绍 多数据源字面意思,比如说二个数据库,甚至不同类型的数据库.在用SpringBoot开发项目时,随着业务量的扩大,我们通常会进行数据库拆分或是引入其他数据库,从而我们需要配置多个数据源. ...
- 【原】通过AOP实现MyBatis多数据源的动态切换
[环境参数]1.开发框架:Spring + SpringMVC + MyBatis 2.数据库A的URL:jdbc.url=jdbc:mysql://172.16.17.164:3306/ test? ...
- Spring多数据源的动态切换
Spring多数据源的动态切换 目前很多项目中只能配置单个数据源,那么如果有多个数据源肿么办?Spring提供了一个抽象类AbstractRoutingDataSource,为我们很方便的解决了这个问 ...
- Spring简单实现数据源的动态切换
Spring简单实现数据源的动态切换: 1. 创建一个数据源切换类: 2. 继承AbstractRoutingDataSource,创建多数据源路由类,并注入到spring的配置文件中: 3. AOP ...
- Spring AOP动态切换数据源
现在稍微复杂一点的项目,一个数据库也可能搞不定,可能还涉及分布式事务什么的,不过由于现在我只是做一个接口集成的项目,所以分布式就先不用了,用Spring AOP来达到切换数据源,查询不同的数据库就可以 ...
- SpringBoot多数据源:动态数据源
目录 1. 引言 2. 动态数据源流程说明 3. 实现动态数据源 3.1 说明及数据源配置 3.1.1 包结构说明 3.1.2 数据库连接信息配置 3.1.3 数据源配置 3.2 动态数据源设置 3. ...
- Spring配置多个数据源,并实现数据源的动态切换转载)
1.首先在config.properties文件中配置两个数据库连接的基本数据.这个省略了 2.在spring配置文件中配置这两个数据源: 数据源1 <!-- initialSize初始化时建立 ...
随机推荐
- NVM 安装 nodejs
Windows 安装: 下载NVM 安装包:https://github.com/coreybutler/nvm-windows/releases 下载nvm-setup.zip文件后,解压后安装 安 ...
- 处理程序“SimpleHandlerFactory-Integrated”在其模块列表中有一个错误模块“ManagedPipelineHandler” 先装 .Net 后装 IIS
以管理员身份打开 cmd 运行 cd C:\Windows\Microsoft.NET\Framework\v4.0.30319 运行 aspnet_regiis.exe -i 重新注册 原因是先 ...
- Shell脚本常用模板
作为一个运维人员编写Shell脚本是很平常的,一个格式好的脚本不仅赏心悦目,后期自己和别人也易于维护. 下面的脚本就是我自己的shell编写格式,如下: [root@mini05 -]# cat te ...
- LeetCode算法题-Word Pattern(Java实现)
这是悦乐书的第202次更新,第212篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第68题(顺位题号是290).给定一个模式和一个字符串str,找到str是否完全匹配该模 ...
- JavaScript数据类型之布尔类型
引言 布尔值指代真或假.开或关.是或否.这个类型只有两个值,保留字true和false.JavaScript程序中的比较语句的结果通常都是布尔值.布尔值通常用于JavaScript中的控制结构中. 真 ...
- 《Java大学教程》—第10章 图形和事件驱动程序
10.2 Swing程序包AWT(Abstract Window Toolkit): 抽象窗口工具集.依赖于本地操作系统的组件被称为重量级(Heavy Weight)组件,因为它们都会大量使用系统资源 ...
- TIPS FOR IMPROVING PERFORMANCE OF KAFKA PRODUCER
When we are talking about performance of Kafka Producer, we are really talking about two different t ...
- MySQL高级知识系列目录
MySQL高级知识(一)——基础 MySQL高级知识(二)——Join查询 MySQL高级知识(三)——索引 MySQL高级知识(四)——Explain MySQL高级知识(五)——索引分析 MySQ ...
- RocketMQ实现事务消息
在RocketMQ4.3.0版本后,开放了事务消息这一特性,对于分布式事务而言,最常说的还是二阶段提交协议,那么RocketMQ的事务消息又是怎么一回事呢,这里主要带着以下几个问题来探究一下Rocke ...
- [HAOI2018]染色
嘟嘟嘟 这题当时没想出来(因为本人实在不太擅长计数),然后又被luogu的第一篇题解吓怕了,就咕了一小段时间再写. 其实这题不是很难. 做法就是基础容斥+NTT. 首先出现\(S\)次的颜色最多有\( ...