文章转自 https://blog.csdn.net/neosmith/article/details/61202084

网上的文章基本上都是只有多数据源或只有动态数据源,而最近的项目需要同时使用两种方式,记录一下配置方法供大家参考。

应用场景

项目需要同时连接两个不同的数据库A, B,并且它们都为主从架构,一台写库,多台读库。

多数据源

首先要将spring boot自带的DataSourceAutoConfiguration禁掉,因为它会读取application.properties文件的spring.datasource.*属性并自动配置单数据源。在@SpringBootApplication注解中添加exclude属性即可:

@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class
})
public class TitanWebApplication {
public static void main(String[] args) {
SpringApplication.run(TitanWebApplication.class, args);
}
}

然后在application.properties中配置多数据源连接信息:

# titan库
spring.datasource.titan-master.url=jdbc:mysql://X.X.X.X:port/titan?characterEncoding=UTF-8
spring.datasource.titan-master.username=
spring.datasource.titan-master.password=
spring.datasource.titan-master.driver-class-name=com.mysql.jdbc.Driver
# 连接池配置
# 省略 # 其它库
spring.datasource.db2.url=jdbc:mysql://X.X.X.X:port/titan2?characterEncoding=UTF-8
spring.datasource.db2.username=
spring.datasource.db2.password=
spring.datasource.db2.driver-class-name=com.mysql.jdbc.Driver

由于我们禁掉了自动数据源配置,因些下一步就需要手动将这些数据源创建出来:

@Configuration
public class DataSourceConfig { @Bean(name = "titanMasterDS")
@ConfigurationProperties(prefix = "spring.datasource.titan-master") // application.properteis中对应属性的前缀
public DataSource dataSource1() {
return DataSourceBuilder.create().build();
} @Bean(name = "ds2")
@ConfigurationProperties(prefix = "spring.datasource.db2") // application.properteis中对应属性的前缀
public DataSource dataSource2() {
return DataSourceBuilder.create().build();
} }

接下来需要配置两个mybatis的SqlSessionFactory分别使用不同的数据源:

@Configuration
@MapperScan(basePackages = {"titan.mapper"}, sqlSessionFactoryRef = "sqlSessionFactory1")
public class MybatisDbAConfig { @Autowired
@Qualifier("titanMasterDS")
private DataSource ds1; @Bean
public SqlSessionFactory sqlSessionFactory1() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(ds1); // 使用titan数据源, 连接titan库 return factoryBean.getObject(); } @Bean
public SqlSessionTemplate sqlSessionTemplate1() throws Exception {
SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactory1()); // 使用上面配置的Factory
return template;
}
}

经过上面的配置后,titan.mapper下的Mapper接口,都会使用titan数据源。同理可配第二个SqlSessionFactory:

@Configuration
@MapperScan(basePackages = {"other.mapper"}, sqlSessionFactoryRef = "sqlSessionFactory2")
public class MybatisDbBConfig {
@Autowired
@Qualifier("ds2")
private DataSource ds2; @Bean
public SqlSessionFactory sqlSessionFactory2() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(ds2); return factoryBean.getObject(); } @Bean
public SqlSessionTemplate sqlSessionTemplate2() throws Exception {
SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactory2());
return template;
}
}

完成这些配置后,假设有2个Mapper titan.mapper.UserMapperother.mapper.RoleMapper,使用前者时会自动连接titan库,后者连接ds2库。

动态数据源

使用动态数据源的初衷,是能在应用层做到读写分离,即在程序代码中控制不同的查询方法去连接不同的库。除了这种方法以外,数据库中间件也是个不错的选择,它的优点是数据库集群对应用来说只暴露为单库,不需要切换数据源的代码逻辑。

我们通过自定义注解 + AOP的方式实现数据源动态切换。

首先定义一个ContextHolder, 用于保存当前线程使用的数据源名:

public class DataSourceContextHolder {
public static final Logger log = LoggerFactory.getLogger(DataSourceContextHolder.class); /**
* 默认数据源
*/
public static final String DEFAULT_DS = "titan-master"; private static final ThreadLocal<String> contextHolder = new ThreadLocal<>(); // 设置数据源名
public static void setDB(String dbType) {
log.debug("切换到{}数据源", dbType);
contextHolder.set(dbType);
} // 获取数据源名
public static String getDB() {
return (contextHolder.get());
} // 清除数据源名
public static void clearDB() {
contextHolder.remove();
}
}

然后自定义一个javax.sql.DataSource接口的实现,这里只需要继承Spring为我们预先实现好的父类AbstractRoutingDataSource即可:

public class DynamicDataSource extends AbstractRoutingDataSource {
private static final Logger log = LoggerFactory.getLogger(DynamicDataSource.class); @Override
protected Object determineCurrentLookupKey() {
log.debug("数据源为{}", DataSourceContextHolder.getDB()); return DataSourceContextHolder.getDB();
} }

创建动态数据源:

/**
* 动态数据源: 通过AOP在不同数据源之间动态切换
* @return
*/
@Bean(name = "dynamicDS1")
public DataSource dataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
// 默认数据源
dynamicDataSource.setDefaultTargetDataSource(dataSource1()); // 配置多数据源
Map<Object, Object> dsMap = new HashMap(5);
dsMap.put("titan-master", dataSource1());
dsMap.put("ds2", dataSource2()); dynamicDataSource.setTargetDataSources(dsMap); return dynamicDataSource;
}

自定义注释@DS用于在编码时指定方法使用哪个数据源:

@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.METHOD
})
public @interface DS {
String value() default "titan-master";
}

切面,实现切换逻辑:

@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中愉快的切换数据源了:

@Autowired
private UserAModelMapper userAMapper; @DS("titan-master")
public String ds1() {
return userAMapper.selectByPrimaryKey(1).getName();
} @DS("ds2")
public String ds2() {
return userAMapper.selectByPrimaryKey(1).getName();
}

Spring Boot + Mybatis多数据源和动态数据源配置的更多相关文章

  1. Spring Boot数据访问之动态数据源切换之使用注解式AOP优化

    在Spring Boot数据访问之多数据源配置及数据源动态切换 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)中详述了如何配置多数据源及多数据源之间的动态切换.但是需要读数据库的地方,就 ...

  2. Spring Boot 2.x Redis多数据源配置(jedis,lettuce)

    Spring Boot 2.x Redis多数据源配置(jedis,lettuce) 96 不敢预言的预言家 0.1 2018.11.13 14:22* 字数 65 阅读 727评论 0喜欢 2 多数 ...

  3. Spring Boot数据访问之多数据源配置及数据源动态切换

    如果一个数据库数据量过大,考虑到分库分表和读写分离需要动态的切换到相应的数据库进行相关操作,这样就会有多个数据源.对于一个数据源的配置在Spring Boot数据访问之数据源自动配置 - 池塘里洗澡的 ...

  4. spring boot mybatis XML文件读取properties配置信息

    配置文件application.properties中相关配置信息可以在部署以后修改,引用配置信息可以在代码和mybatis的映射文件中 1.JAVA代码 可以通过变量去读取 application. ...

  5. 分布式事务、多数据源、分库分表中间件之spring boot基于Atomikos+XADataSource分布式事务配置(100%纯动态)

    本文描述spring boot基于Atomikos+DruidXADataSource分布式事务配置(100%纯动态),也就是增加.减少数据源只需要修改application.properties文件 ...

  6. Spring Boot + Mybatis 实现动态数据源

    动态数据源 在很多具体应用场景的时候,我们需要用到动态数据源的情况,比如多租户的场景,系统登录时需要根据用户信息切换到用户对应的数据库.又比如业务A要访问A数据库,业务B要访问B数据库等,都可以使用动 ...

  7. Spring Boot 入门系列(二十三)整合Mybatis,实现多数据源配置!

    d之前介绍了Spring Boot 整合mybatis 使用注解方式配置的方式实现增删改查以及一些复杂自定义的sql 语句 .想必大家对spring boot 项目中,如何使用mybatis 有了一定 ...

  8. Spring Boot + MyBatis + Pagehelper 配置多数据源

    前言: 本文为springboot结合mybatis配置多数据源,在项目当中很多情况是使用主从数据源来读写分离,还有就是操作多库,本文介绍如何一个项目同时使用2个数据源. 也希望大家带着思考去学习!博 ...

  9. Spring Boot + Mybatis 配置多数据源

    Spring Boot + Mybatis 配置多数据源 Mybatis拦截器,字段名大写转小写 package com.sgcc.tysj.s.common.mybatis; import java ...

随机推荐

  1. Install/Remove of the Service Denied!

    在windos 的cmd下安装mysql 在mysql的bin目录下面执行: mysqld --install 报错: 信息如下一: Install/Remove of the Service Den ...

  2. 【Redis】5、Redis事务处理

    MULTI .EXEC .DISCARD 和WATCH 是 Redis 事务的基础 1.MULTI  命令用于开启一个事务,它总是返回 OK .MULTI 执行之后,客户端可以继续向服务器发送任意多条 ...

  3. 2018-02-18 Antlr4实现简单语言之条件语句

    本系列之前的文章: Antlr4的分析错误处理 Antlr4实现数学四则运算 Antlr4添加中文变量赋求值,括号,各种问题 Antlr4: 修改语法规则更接近普通BNF格式 Antlr4实现简单语言 ...

  4. 【读书笔记】iOS-基带攻击

    一,iOS设备中的蜂窝网络通信栈运行在专门的芯片上,这个芯片就是数字基带处理器. 参考资料:<黑客攻防技术宝典-iOS实战篇>

  5. 移动端reset

    * { margin: 0; padding: 0;}article, aside, details, figcaption, figure, footer, header, hgroup, main ...

  6. 什么是DDoS攻击?DDoS防御的11种方针详解

    对于遭受DDOS攻击的情况是让人很尴尬的,如果我们有良好的DDoS防御方法,那么很多问题就将迎刃而解,我们来看看我们有哪些常用的有效地方法来做好DDoS防御呢. 对于DDoS防御的理解: 对付DDOS ...

  7. (网页)js每隔5分钟执行一次ajax请求的实现方法(转)

    转自脚本之家: 一个页面好像只能有一个 window.onload=function(){},所以要有多个事件,这样写就好了 window.onload=function(){ //假设这里每个五分钟 ...

  8. TensorFlow实现梯度下降

    # -*- coding: utf-8 -*- """ Created on Mon Oct 15 17:38:39 2018 @author: zhen "& ...

  9. 洗礼灵魂,修炼python(42)--巩固篇—type内置函数与类的千丝万缕关系

    type函数的隐藏属性 相信大家都知道内置函数type是用来查看对象的数据类型的.例: 那比如我对int类查看类型呢? 有朋友会说,int是内置类啊,用自定义的应该不会这样,我们自定义一个类呢? 还是 ...

  10. SMP多核启动

    在 Linux系统中,对于多核的ARM芯片而言,在Biotron代码中,每个CPU都会识别自身ID,如果ID是0,则引导Bootloader和 Linux内核执行,如果ID不是0,则Biotron一般 ...