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初始化时建立 ...
随机推荐
- SqlServer 线下讲座
2017年有幸在某互联网公司及其子公司进行了一次技术分享性质的讲座,讲座内容主要针对sqlserver 2017以及azure sql 的一些技术特性,进一步展示sql server 及其相关产品的新 ...
- C#计算时间差 TimeSpan
TimeSpan的相关属性 Add:与另一个TimeSpan值相加. Days:返回用天数计算的TimeSpan值. Duration:获取TimeSpan的绝对值. Hours:返回用小时计算的Ti ...
- 常见C语言内存错误
前言 C语言强大的原因之一在于几乎能掌控所有的细节,包括对内存的处理,什么时候使用内存,使用了多少内存,什么时候该释放内存,这都在程序员的掌控之中.而不像Java中,程序员是不需要花太多精力去处理垃圾 ...
- JAVA 容易忽略的东西
Java中的取余会出现负数.用Math.floorMod()方法可以掰正,但是也仅限被除数是负数的情况,如果除数是负数,这个没用. 和C不一样,Java中的字符串是不可变字符串,不能修改Java字符串 ...
- spring简述
1. 什么是Spring Spring:SE/EE开发的一站式框架.(有EE开发每一层的解决方案) WEB层:SpringMVC Service层:Spring的Bean管理,Spring的声明式事务 ...
- 服务创建&删除
创建服务.bat @echo.服务启动...... @echo off @sc create BestoneProductEditSvc binPath= "D:\winSvc\Beston ...
- 使用Docker在本地搭建Hadoop分布式集群
学习Hadoop集群环境搭建是Hadoop入门必经之路.搭建分布式集群通常有两个办法: 要么找多台机器来部署(常常找不到机器) 或者在本地开多个虚拟机(开销很大,对宿主机器性能要求高,光是安装多个虚拟 ...
- (转)ElasticSearch教程——汇总篇
https://blog.csdn.net/gwd1154978352/article/details/82781731 环境搭建篇 ElasticSearch教程——安装 ElasticSearch ...
- WPFの无边框窗体以及控件的移动
对于WPF,一旦隐藏了标题栏,就无法移动,这时候需要重写移动方法,下面列举常见的三种方式方式. 方式一:重写OnMouseLeftButtonDown protected override void ...
- sqrt函数
import numpy as np B = np.arange(3) print (B) print (np.sqrt(B)) #求平方根