-
2020-05-15 23:18:41
前段时间同事在项目当中使用到多数据源,项目集成的是mybatis plus ,遇到一个非常神仙的BUG,有数据源A ,数据源B,针对数据源A的所有操作都没有问题,针对数据源B的所有操作,除了批量的操作也都没有问题!!!
当进行批量操作的时候,发现会提示,xxx表不存在!!!,但是如果是查询数据,不批量进行操作,就可以查询出来,所以当遇到这个神仙BUG的时候,直接蒙圈了!!!
最后进行到处尝试,分析源码,等待各种方式,搞了一两天,终于知道原因了,mybatis plus里面的批量操作都没有走DAO层,是通过SqlSession进行的,然后数据源的切换是在DAO进行的。
分析到上面,本以为找到了问题,发现数据源的切换是手写的一个注解,这个注解在其他项目当中是完全没有出现这个问题的!!!最后一步一步跟踪代码分析发现主要原因是在项目配置多数据源的时候,会指定每个数据源的DAO层接口位置。然后将DAO接口位置和数据源的关系存储起来,然后调用DAO层的时候就调用对应的数据源。但是在这个项目当中,DAO层没有分包,两个数据源指定的DAO层位置都一样,就导致出现这个问题。
最终解决方式:DAO层按照数据源分包就可以了。
总结:按照规范来说项目多数据源的时候都是需要进行分包的,这样比较容易区分管理。也会避免很多不必要的问题!!!
更多相关内容 -
springboot-整合多数据源配置
2020-10-20 11:21:58} } 2.3 给使用非默认数据源添加注解@DS @DS 可以注解在方法上和类上,同时存在方法注解优先于类上注解。 注解在 service 实现或 mapper 接口方法上,不要同时在 service 和 mapper 注解。 @DS("db2") public ...简介
主要介绍两种整合方式,分别是 springboot+mybatis 使用分包方式整合,和 springboot+druid+mybatisplus 使用注解方式整合。
一、表结构
在本地新建两个数据库,名称分别为
db1
和db2
,新建一张user
表,表结构如下:
SQL代码:CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键', `name` varchar(25) NOT NULL COMMENT '姓名', `age` int(2) DEFAULT NULL COMMENT '年龄', `sex` tinyint(1) NOT NULL DEFAULT '0' COMMENT '性别:0-男,1-女', `addr` varchar(100) DEFAULT NULL COMMENT '地址', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
二、多数据源整合
1. springboot+mybatis使用分包方式整合
1.1 主要依赖包
- spring-boot-starter-web
- mybatis-spring-boot-starter
- mysql-connector-java
- lombok
pom.xml
文件如下:<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.9.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>multipledatasource</artifactId> <version>0.0.1-SNAPSHOT</version> <name>multipledatasource</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <!-- spring 依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- mysql 依赖 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
1.2 application.yml 配置文件
server: port: 8080 # 启动端口 spring: datasource: db1: # 数据源1 jdbc-url: jdbc:mysql://localhost:3306/db1?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=GMT%2B8 username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver db2: # 数据源2 jdbc-url: jdbc:mysql://localhost:3306/db2?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=GMT%2B8 username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver
注意事项
- 各个版本的 springboot 配置 datasource 时参数有所变化,例如低版本配置数据库 url 时使用 url 属性,高版本使用 jdbc-url 属性,请注意区分。
1.3 建立连接数据源的配置文件
第一个配置文件
@Configuration @MapperScan(basePackages = "com.example.multipledatasource.mapper.db1", sqlSessionFactoryRef = "db1SqlSessionFactory") public class DataSourceConfig1 { @Primary // 表示这个数据源是默认数据源, 这个注解必须要加,因为不加的话spring将分不清楚那个为主数据源(默认数据源) @Bean("db1DataSource") @ConfigurationProperties(prefix = "spring.datasource.db1") //读取application.yml中的配置参数映射成为一个对象 public DataSource getDb1DataSource(){ return DataSourceBuilder.create().build(); } @Primary @Bean("db1SqlSessionFactory") public SqlSessionFactory db1SqlSessionFactory(@Qualifier("db1DataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); // mapper的xml形式文件位置必须要配置,不然将报错:no statement (这种错误也可能是mapper的xml中,namespace与项目的路径不一致导致) bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/db1/*.xml")); return bean.getObject(); } @Primary @Bean("db1SqlSessionTemplate") public SqlSessionTemplate db1SqlSessionTemplate(@Qualifier("db1SqlSessionFactory") SqlSessionFactory sqlSessionFactory){ return new SqlSessionTemplate(sqlSessionFactory); } }
第二个配置文件
@Configuration @MapperScan(basePackages = "com.example.multipledatasource.mapper.db2", sqlSessionFactoryRef = "db2SqlSessionFactory") public class DataSourceConfig2 { @Bean("db2DataSource") @ConfigurationProperties(prefix = "spring.datasource.db2") public DataSource getDb1DataSource(){ return DataSourceBuilder.create().build(); } @Bean("db2SqlSessionFactory") public SqlSessionFactory db1SqlSessionFactory(@Qualifier("db2DataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/db2/*.xml")); return bean.getObject(); } @Bean("db2SqlSessionTemplate") public SqlSessionTemplate db1SqlSessionTemplate(@Qualifier("db2SqlSessionFactory") SqlSessionFactory sqlSessionFactory){ return new SqlSessionTemplate(sqlSessionFactory); } }
1.4 具体实现
注意事项
- 在 service 层中根据不同的业务注入不同的 dao 层
- 如果是主从复制- -读写分离:比如 db1 中负责增删改,db2 中负责查询。但是需要注意的是负责增删改的数据库必须是主库(master)
2. springboot+druid+mybatisplus使用注解整合
2.1 主要依赖包
- spring-boot-starter-web
- mybatis-plus-boot-starter
- dynamic-datasource-spring-boot-starter # 配置动态数据源
- druid-spring-boot-starter # 阿里的数据库连接池
- mysql-connector-java
- lombok
pom.xml
文件如下:<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.9.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>mutipledatasource2</artifactId> <version>0.0.1-SNAPSHOT</version> <name>mutipledatasource2</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.2.0</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>2.5.6</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.20</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <profiles> <profile> <id>local1</id> <properties> <profileActive>local1</profileActive> </properties> <activation> <activeByDefault>true</activeByDefault> </activation> </profile> <profile> <id>local2</id> <properties> <profileActive>local2</profileActive> </properties> </profile> </profiles> </project>
2.2 application.yml 配置文件
server: port: 8080 spring: datasource: dynamic: primary: db1 # 配置默认数据库 datasource: db1: # 数据源1配置 url: jdbc:mysql://localhost:3306/db1?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=GMT%2B8 username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver db2: # 数据源2配置 url: jdbc:mysql://localhost:3306/db2?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=GMT%2B8 username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver durid: initial-size: 1 max-active: 20 min-idle: 1 max-wait: 60000 autoconfigure: exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 去除druid配置
DruidDataSourceAutoConfigure
会注入一个DataSourceWrapper
,其会在原生的spring.datasource
下找 url, username, password 等。动态数据源 URL 等配置是在 dynamic 下,因此需要排除,否则会报错。排除方式有两种,一种是上述配置文件排除,还有一种可以在项目启动类排除:
@SpringBootApplication(exclude = DruidDataSourceAutoConfigure.class) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
2.3 给使用非默认数据源添加注解@DS
@DS
可以注解在方法上和类上,同时存在方法注解优先于类上注解。
注解在 service 实现或 mapper 接口方法上,不要同时在 service 和 mapper 注解。@DS("db2") public interface UserMapper extends BaseMapper<User> { } @Service @DS("db2") public class ModelServiceImpl extends ServiceImpl<ModelMapper, Model> implements IModelService {} @Select("SELECT * FROM user") @DS("db2") List<User> selectAll();
-
JdbcTemplate动态多数据源配置
2019-09-04 22:44:24数据源就是javax.sql.DataSource,所有实现了这个接口的DataSource就叫做数据源,现在比较常用阿里巴巴的DruidDataSource,支持监控多数据源下的sql运行状况,便于以此实现以sql为核心的应用系统,比如BI报表系统、...一、前言
多数据源的配置,是一个相对比较常见的需求。
什么是数据源?数据源就是javax.sql.DataSource,所有实现了这个接口的DataSource就叫做数据源,现在比较常用阿里巴巴的DruidDataSource,支持监控多数据源下的sql运行状况,便于以此实现以sql为核心的应用系统,比如BI报表系统、BI工具、ETL工具等。而这些场景下的业务sql通常是属于动态数据源,它们的操作对象来自于不同的数据库类型,或不同的数据库实例,通常被存放在某个业务表中,还可能需要被新增、删除和修改,因而我们不能像使用Mybatis那样,预先定义好要执行的所有sql,然后放在Mybatis的mapper配置文件中,这种Jdbc操作方式就显得不恰当,换用spring-jdbc的JdbcTemplate刚好可以完美地解决这个问题。
二、实现思路
2.1 配置数据源属性
这里选用DruidDataSource作为我们的业务数据源,一般在application.properties中为其配置各项Druid连接池属性,如下:
2.2 自定义业务数据源Bean
实现自己的DruidDataSource,加载上面的Druid数据源配置,初始化DruidDataSource实例,以线程安全的方式缓存在一Map<String, DruidDataSource>中,并提供获取数据源和关闭数据源的方法。如下:
package com.cjia.common.jdbc; import java.sql.DriverManager; import java.sql.SQLException; import java.util.HashMap; import java.util.Map; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.util.StringUtils; import com.cjia.common.exception.SourceException; import com.cjia.model.SqlDBConfig; import com.cjia.utils.MD5Util; import lombok.extern.slf4j.Slf4j; @Slf4j @Component public class JdbcDataSource extends DruidDataSource { @Value("${spring.datasource.type}") private String type; @Value("${source.max-active:10}") private int maxActive; @Value("${source.initial-size:5}") private int initialSize; @Value("${source.min-idle:3}") private int minIdle; @Value("${source.max-wait:30000}") private int maxWait; @Value("${spring.datasource.druid.time-between-eviction-runs-millis}") private int timeBetweenEvictionRunsMillis; @Value("${spring.datasource.druid.min-evictable-idle-time-millis}") private int minEvictableIdleTimeMillis; @Value("${spring.datasource.druid.test-while-idle}") private boolean testWhileIdle; @Value("${spring.datasource.druid.test-on-borrow}") private boolean testOnBorrow; @Value("${spring.datasource.druid.test-on-return}") private boolean testOnReturn; @Value("${spring.datasource.druid.filters}") private String filters; @Value("${source.break-after-acquire-failure:true}") private boolean breakAfterAcquireFailure; @Value("${source.connection-error-retry-attempts:0}") private int connectionErrorRetryAttempts; private static volatile Map<String, DruidDataSource> map = new HashMap<>(); public synchronized void removeDatasource(String jdbcUrl, String username, String password) { String key = getKey(jdbcUrl, username, password); if (map.containsKey(key)) { map.remove(key); } } public synchronized DruidDataSource getDataSource(SqlDBConfig sqlDBConfig) throws SourceException { String jdbcUrl = sqlDBConfig.getUrl(); String username = sqlDBConfig.getUsername(); String password = sqlDBConfig.getPassword(); String key = getKey(jdbcUrl, username, password); if (!map.containsKey(key) || null == map.get(key)) { DruidDataSource instance = new JdbcDataSource(); String className = null; try { className = DriverManager.getDriver(jdbcUrl.trim()).getClass().getName(); } catch (SQLException e) { } if (StringUtils.isEmpty(className)) { throw new SourceException("Driver Class Not null: DbId=" + sqlDBConfig.getDbId()); } else { instance.setDriverClassName(className); } instance.setUrl(jdbcUrl.trim()); instance.setUsername(username); instance.setPassword(password); instance.setInitialSize(initialSize); instance.setMinIdle(minIdle); instance.setMaxActive(maxActive); instance.setMaxWait(maxWait); instance.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); instance.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); instance.setTestWhileIdle(false); instance.setTestOnBorrow(testOnBorrow); instance.setTestOnReturn(testOnReturn); try { instance.setFilters(filters); } catch (SQLException ex) { log.error("druid configuration initialization filter", ex); } instance.setConnectionErrorRetryAttempts(connectionErrorRetryAttempts); instance.setBreakAfterAcquireFailure(breakAfterAcquireFailure); try { instance.init(); } catch (Exception e) { log.error("Exception during pool initialization", e); throw new SourceException(e.getMessage()); } map.put(key, instance); } return map.get(key); } private String getKey(String jdbcUrl, String username, String password) { StringBuilder sb = new StringBuilder(); if (!StringUtils.isEmpty(username)) { sb.append(username); } if (!StringUtils.isEmpty(password)) { sb.append(":").append(password); } sb.append("@").append(jdbcUrl.trim()); return MD5Util.md5(sb.toString(), MD5Util.SEC_KEY); } }
2.3 动态构造JdbcTemplate
首先,注入上一步自定义的业务数据源JdbcDataSource,接着,JdbcTemplate提供了传入DataSource的构造方式,获取到JdbcTemplate对象后,调用它丰富的API执行业务sql,如下:
// 获取初始化后的druid数据源,SqlDBConfig存放了jdbcUrl、username、password DruidDataSource dataSource = jdbcDataSource.getDataSource(sqlDBConfig); List<Map<String, Object>> result = new JdbcTemplate(dataSource).queryForList(sql, paramArgs);
以上。
-
Springboot实现多数据源整合的两种方式
2021-11-02 21:43:39大家好,我是小诚,不知不觉上一次更文已经是20多天前了!其实这段时间也一直没有闲着,一个是在梳理之前的文章知识和资源,用于搭建技术圈子,另外一个就是在思考自己的一个输出方向,社区发展得很迅速,热榜各种...一、前言
-
大家好,我是小诚,不知不觉上一次更文已经是20多天前了!其实这段时间也一直没有闲着,
一个是在梳理之前的文章知识和资源,用于搭建技术圈子,另外一个就是在思考自己的一个输出方向,社区发展得很迅速,热榜各种各类的文章都有,深思熟虑后,还是坚持文章在精不在多,质量标准更加重要,所以今后博文的方向会更加偏向实战和经验,争取分享更加有价值的博文!
-
如果文章对你有帮助,可以帮忙一键三连和专栏订阅哦!
技术圈子经过这段时间的筹划,已经初步成型!有兴趣、志同道合的小伙伴可以查看左边导航栏的技术圈子介绍,期待你们的加入! -
本篇文章重点介绍SpringBoot集合MyBatis和MyBatis-Plus整合多数据源方面的知识!
🚅 二、专栏推荐
良心推荐: 下面的相关技术专栏还在免费分享哦,大家可以帮忙点点订阅哦!
🚔 三、整合多数据源需要了解的知识
1、何时会使用到多数据源
一个技术的出现、应用必然是为了解决存在的某些问题,多数据源出现常见的场景如下:
(1)、与第三方对接时,有些合作方并不会为了你的某些需求而给你开发一个功能,他们可以提供给你一个可以访问数据源的只读账号,你需要获取什么数据由你自己进行逻辑处理,这时候就避免不了需要进行多数据源整合了。
(2)、业务数据达到了一个量级,使用单一数据库存储达到了一个瓶颈,需要进行分库分表等操作进行数据管理,在操作数据时,不可避免的涉及到多数据源问题。
2、多数据源整合有哪些方式
参考了网上的许多材料,发现整合方式无外乎以下几种:
(1)、使用分包方式,不同的数据源配置不同的MapperScan和mapper文件
(2)、使用APO切片方式,实现动态数据源切换(如果对Aop不是很熟悉,欢迎查看我之前的一篇文章,这知识保熟哦!【什么是面向切面编程?】)
(3)、使用数据库代理中间件,如Mycat等
3、不同方式之间的区别
(1)、分包方式可以集合JTA(JAVA Transactional API)实现分布式事务,但是整个流程的实现相对来说比较复杂。
(2)、AOP动态配置数据源方式缺点在于无法实现全局分布式事务,所以如果只是对接第三方数据源,不涉及到需要保证分布式事务的话,是可以作为一种选择。
(3)、
使用数据库代理中间件方式是现在比较流行的一种方式,很多大厂也是使用这种方式
,开发者不需要关注太多与业务无关的问题,把它们都交给数据库代理中间件去处理,大量的通用的数据聚合,事务,数据源切换都由中间件来处理,中间件的性能与处理能力将直接决定应用的读写性能,比较常见的有Mycat、TDDL等。现在阿里出了100%自研的分布式数据库OceanBase,从最底层支持分布式,性能也非常强大,大家感兴趣的可以去了解下!
4、本文实战选择的方式
鉴于本次遇到需求的整合多数据源的场景是需要
对接第三方的数据
,暂不涉及到分布式事务问题 ,所以本文实战整合多数据源使用的方式是【分包方式】实现简单的多数据源整合,至于其他方式和分布式事务的坑,后面再慢慢填吧(o(╥﹏╥)o)!
🚢 四、SpringBoot+MyBatis整合多数据源
🔴 4.1 说明
本次案例涉及到的代码比较多,因此文章只贴出部分,全部案例代码已经上传到Gitee,需要者可直接访问:【SpringBoot结合MyBatis整合多数据源】,项目结构如下:
🟠 4.2 涉及依赖包
- spring-boot-starter-web -- web相关支持
- mybatis-spring-boot-starter -- springboot整合mybatis依赖
- mysql-connector-java -- mysql数据驱动
- lombok -- 自动生成实体类常用方法依赖包
- hutool-all -- 常用方法封装依赖包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.13</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.4.5</version> </dependency>
🟡 4.3 项目配置
# 项目启动端口 server: port: 9090 # 项目 名称 spring: application: name: multi-datasource-instance datasource: # 主数据库 master: # 注意,整合多数据源时如果使用springboot默认的数据库连接池Hikari,指定连接数据使用的是jdbc-url而不是url属性 jdbc-url: jdbc:mysql://localhost:3306/test1?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver # 副数据库 slave: # 注意,整合多数据源时如果使用springboot默认的数据库连接池Hikari,指定连接数据使用的是jdbc-url而不是url属性 jdbc-url: jdbc:mysql://localhost:3306/test2?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver
🟢 4.4 编写主副数据库数据源配置
1、主数据源相关配置:
主要是指定主数据源、扫描的mapper地址、事务管理器等信息。@Configuration // 指定主数据库扫描对应的Mapper文件,生成代理对象 @MapperScan(basePackages ="com.diary.it.multi.datasource.mapper" ,sqlSessionFactoryRef = "masterSqlSessionFactory") public class MasterDataSourceConfig { // mapper.xml所在地址 private static final String MAPPER_LOCATION = "classpath*:mapper/*.xml"; /** * 主数据源,Primary注解必须增加,它表示该数据源为默认数据源 * 项目中还可能存在其他的数据源,如获取时不指定名称,则默认获取这个数据源,如果不添加,则启动时候回报错 */ @Primary @Bean(name = "masterDataSource") // 读取spring.datasource.master前缀的配置文件映射成对应的配置对象 @ConfigurationProperties(prefix = "spring.datasource.master") public DataSource dataSource() { DataSource build = DataSourceBuilder.create().build(); return build; } /** * 事务管理器,Primary注解作用同上 */ @Bean(name = "masterTransactionManager") @Primary public PlatformTransactionManager dataSourceTransactionManager(@Qualifier("masterDataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } /** * session工厂,Primary注解作用同上 */ @Bean(name = "masterSqlSessionFactory") @Primary public SqlSessionFactory sqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource) throws Exception { final SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean(); sessionFactoryBean.setDataSource(dataSource); sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MasterDataSourceConfig.MAPPER_LOCATION)); return sessionFactoryBean.getObject(); } }
2、副数据源相关配置:
主要是指定数据源、扫描的mapper地址、事务管理器等信息。@Configuration // 指定从数据库扫描对应的Mapper文件,生成代理对象 @MapperScan(basePackages = "com.diary.it.multi.datasource.mapper2", sqlSessionFactoryRef = "slaveSqlSessionFactory") public class SlaveDataSourceConfig { // mapper.xml所在地址 private static final String MAPPER_LOCATION = "classpath*:mapper2/*.xml"; /** * 数据源 */ @Bean(name = "slaveDataSource") // 读取spring.datasource.slave前缀的配置文件映射成对应的配置对象 @ConfigurationProperties(prefix = "spring.datasource.slave") public DataSource dataSource() { DataSource build = DataSourceBuilder.create().build(); return build; } /** * 事务管理器 */ @Bean(name = "slaveTransactionManager") public PlatformTransactionManager dataSourceTransactionManager(@Qualifier("slaveDataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } /** * session工厂 */ @Bean(name = "slaveSqlSessionFactory") public SqlSessionFactory sqlSessionFactory(@Qualifier("slaveDataSource") DataSource dataSource) throws Exception { final SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean(); sessionFactoryBean.setDataSource(dataSource); sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(SlaveDataSourceConfig.MAPPER_LOCATION)); return sessionFactoryBean.getObject(); } }
4.5 执行结果
完成上面的步骤后,就跟我们平常写业务逻辑的方式一样,在service中写业务逻辑,在mapper中写sql语句等,下面看看执行结果!
4.6 整合中遇到的问题
看完上面的教程,是不是发现其实整合多数据源其实也挺简单的!
但是,完全按照教学流程整合还是遇到各种问题的现象真的太常见了,下面博主就总结下整合中遇到的各种问题
,如果你在整合过程中也遇到了,可以直接按照博主的解决方案来哦(贴心吧!)。问题1、出现 jdbcUrl is required with driverClassName异常
原因: SpringBoot2.x后默认的数据库连接池就是HikariCP(号称史上最快,性能最高),
HikariCP连接池中命名规则和其他的连接池不太一样,指定连接数据库的地址时,它使用的是jdbc-url而不是url,所以如果我们不指定数据库连接池如druid而使用springboot默认的连接池的话,需要将配置中连接数据库的url改成jdbc-url属性。
问题2、 出现 Invalid bound statement (not found)异常
原因:
(1)、在定义数据源配置信息时没有指定SqlSessionFactoryBean扫描的mapper.xml文件的位置即 sessionFactoryBean.setMapperLocations(xxx)。
(2)、mapper.xml文件中namespace属性对应的路径不准确或者对应方法的id名称、parameterType属性不对
(3)、xxxMapper.java的方法返回值是List,而select元素没有正确配置ResultMap,或者只配置ResultType
问题3、 出现 required a single bean, but 2 were found异常
原因: 因为我们在指定主副数据源配置时已经使用MapperScan注解进行扫描对应的mapper.java,此时被扫描到的mapper.java已经生成代理类到Spring容器,如果此时在启动类中再使用MapperScan扫描则会成出现上面的问题(奇怪的是:这个问题我换一台电脑就不报错了,所以出现这个问题先按照这个方案解决吧)
问题4、 主数据源配置类中为什么添加Primary注解
原因: 因为整合了多数据源,所以DataSource、PlatformTransactionManager等实例都会注入多个到Spring容器中,Primary注解的作用就是:
当我们使用自动配置的方式如Autowired注入Bean时,如果这个Bean有多个候选者,如果其中一个候选者具有@Primary注解修饰,该候选者会被选中,作为自动配置的值。
问题5、 com.mysql.jdbc.Driver 和 com.mysql.cj.jdbc.Driver的区别
原因: 细心的小伙伴会发现,在数据库配置中driver-class-name属性的值为com.mysql.cj.jdbc.Driver,其实com.mysql.jdbc.Driver 是 对应mysql-connector-java 5驱动的,com.mysql.cj.jdbc.Driver 是 mysql-connector-java 6及之后的数据库驱动的,如果使用了6.x后的mysql数据库驱动还继续使用com.mysql.jdbc.Driver 则启动时会报deprecated(过时的),同时使用mysql6.x后的驱动需要指定时区serverTimezone:
五、SpringBoot+Mybatis-Plus整合多数据源
上面Mybatis使用分包的方式整合多数据源多少还是有些麻烦的,但是使用MyBatis-Plus就比较简单了,
MyBatis-Plus官方就支持了多数据源,使用的时候只需要一个注解就可以实现,整合多数据源的时候推荐使用该种方式。
🟥 5.1 说明
本次案例涉及到的代码比较多,因此文章只贴出部分,全部案例代码已经上传到Gitee,需要者可直接访问:【实战-SpringBoot结合MyBatis-Plus整合多数据源】,mybatis-plus多数据源支持:
项目结构如下:
🟧 5.2 涉及的依赖包
- spring-boot-starter-web -- web相关支持
- mybatis-plus-boot-starter-- springboot整合mybatis-plus依赖
- dynamic-datasource-spring-boot-starter -- mybatis-plus管理数据源依赖
- mysql-connector-java -- mysql数据驱动
- lombok -- 自动生成实体类常用方法依赖包
- hutool-all -- 常用方法封装依赖包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.4.1</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.3</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.4.5</version> </dependency>
🟨 5.3 相关配置
# 启动端口 server: port: 9091 # 项目名称 spring: application: name: multi-datasource-instance2 datasource: # 采用动态选取 dynamic: primary: master #设置默认的数据源或者数据源组,默认值即为master strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源 datasource: # 主数据库 master: url: jdbc:mysql://localhost:3306/test1?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver # 副数据库 slave: url: jdbc:mysql://localhost:3306/test2?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver
🟩 5.4 使用方式
🟦 5.5 执行结果
-
-
SpringBoot实现多数据源的两种方式
2021-10-22 22:19:53公司项目有连接多个不同数据库的需求,特研究了一下,根据网上的资料,造了一个基于AOP方式的数据源切换轮子,但继续探索,突然发现有开源的多数据源管理启动器。不过,本篇两种方式都会介绍。 基于dynamic-... -
【实战】SpringBoot整合多数据源
2021-09-23 21:55:10项目多数据源业务场景在开发中时常会出现,本文通过实战的方式,介绍SpringBoot结合Mybatis和Mybatis-plus实现多数据源整合应用。 -
SpringBoot多数据源切换,AOP实现动态数据源切换
2018-10-30 16:32:25SpringBoot多数据源切换,AOP实现动态数据源切换 操作数据一般都是在DAO层进行处理,可以选择直接使用JDBC进行编程 或者是使用多个DataSource 然后创建多个SessionFactory,在使用Dao层的时候通过不同的... -
Spring AOP实现多数据源切换
2018-11-30 22:44:07有时候我们会遇到这样的场景:一个应用系统中存在多个数据源,需要根据不同业务场景进行临时切换。比如读写分离(也可以考虑使用Mycat等数据库中间件)等。 Spring提供了动态数据源的功能,可以让我们实现在对... -
关于springboot+mybatis 配置多数据源打印sql语句失效的问题
2019-09-09 16:08:11关于springboot+mybatis 配置多数据源打印sql语句失效的问题当我们springboot整合mybatis时,可以打印sql语句但是当我们配置多数据源时,使用mybatis.configuration.log-impl无法打印sql语句解决 在数据库配置时结果... -
MyBatis-Plus 高级功能 —— 多数据源配置
2021-02-25 19:09:56那为什么会有这篇文章呢,那是因为小编在使用mybatis-plus这个框架,明明一个多数据源配置很简单,但是网上其他博客着实太过于复杂了。 从数据源配置,每一个数据源配置一个config类,还有需要自己实现AOP的,当时... -
Druid多数据源情况下的数据库重连问题
2019-07-10 12:01:47Druid多数据源情况下的数据库重连问题 上一篇踩坑日记写完之后,自己反复读了几遍,感觉读起来有点怪怪的,也许是因为自己做程序员久了,口头表达的次数也少了,所以很明显感觉到自己有时候说话不那么顺溜了,想当年... -
多数据源下Seata分布式事务出现的问题和解决方法
2021-10-24 09:14:57一阶段:业务数据和回滚日志记录在同一个本地事务中提交 (本地数据库先保存,并向undo_log表写入日志),释放本地锁和连接资源。 二阶段: 提交异步化,非常快速地完成。 回滚通过一阶段的回滚日志进行反向补偿... -
SpringBoot:多数据源配置——注解+AOP
2018-12-28 22:25:20* 基于Spring提供的AbstractRoutingDataSource,动态添加数据源(事务下可能存在问题) -- DynamicDataSource * 自定义注解,标识数据源 -- TargetDataSource * AOP前后置拦截解析类,对... -
Springboot + HikariCp + MybatisPlus多数据源配置 注解切换数据源 (2)
2019-09-29 21:49:57多数据源实现请参考上文 Springboot + HikariCp + MybatisPlus多数据源配置 (1) 由于手动切换在我看来并不优雅,但他最主要的是能够让我少写点代码。所以出现了Aop 首先引入aop依赖 <dependency> <... -
Spring项目中使用两种方法动态切换数据源,多数据源切换
2018-08-13 22:03:55本文介绍两种切换数据库的方法。 方法1:数据库信息都配置在spring xml中,适用于一般数据库切换。...方法2:将数据库信息配置在默认数据源中,适用于切换数据库操作同一方法,相当于批量执行方法。... -
Springboot+MyBatis-Plus+druid多数据源配置实现
2020-08-10 19:01:03Springboot+MyBatis-Plus+druid多数据源配置实现 一.背景 业务上需求或者业务的扩展需要分库,多数据源的配置使用在业务开发中常常会用到。MyBatis-plus官网... -
springboot2多数据源完整示例
2018-11-08 14:25:17springboot2 + mybatis + mysql + oracle多数据源的配置 相信很多朋友在开发的时候,可能会碰到需要一个项目,配置多个数据源的需求,可能是同一种数据库,也可能是不同种类的数据库。 这种情况,我们就需要配置多... -
dynamic-datasource-spring-boot-starter 动态加载多数据源问题处理
2022-04-20 17:08:24本文主要阐述用dynamic-datasource-spring-boot-starter配置多数据源时,按需动态初始化数据库or按需加载数据源的问题处理。 背景:有个做数据处理的组件,数据来源是某几个数据库,这时候可用dynamic-datasource-... -
ShardingSphere jdbc集成多数据源
2020-08-02 13:39:35ShardingSphere今年4月份成为了 Apache 软件基金会的顶级项目,目前支持数据分片、读写分离、多数据副本、数据加密、影子库压测等功能,同时兼容多种数据库,通过可插拔架构,理想情况下,可以做到对业务代码无感知... -
使用JDBC实现动态的多数据源
2019-06-17 23:12:07} } // 创建数据源 private void createConnection(String databaseName){ ComboPooledDataSource dataSource = new ComboPooledDataSource(); try { dataSource.setDriverClass(driverName); } catch ... -
SpringBoot动态配置多数据源-从数据库读取连接信息
2019-09-03 23:09:27throw new ADIException("不存在的数据源:"+datasource,500); } } else { log.info("---当前数据源:默认数据源---"); } } return datasource; } @Override public void ... -
Springboot 从数据库读取数据库配置信息,动态切换多数据源 最详细实战教程
2019-10-23 13:38:00以前写过一篇教程,Springboot AOP方式切换多数据源(主从两库类似情况使用最佳): https://blog.csdn.net/qq_35387940/article/details/100122788 网上大多流传的springboot系列的切换多数据源都是以上那种写死... -
springboot-AOP 切换多数据源时 @Transactional 导致切换数据源失败的问题--已解决
2020-06-16 16:51:55项目有多个数据源, 根据配置文件配置的连接数来自动生成多数据源配置 并且使用 aop切换数据源,具体实现方式 网上有很多 使用的是 AbstractRoutingDataSource 重写 determineCurrentLookupKey 方法 默认数据源为... -
SpringBoot多数据源切换详解,以及开启事务后数据源切换失败处理
2019-11-21 15:37:47最近项目需要指出多数据源,同时支持事务回滚,这里记录一下 1、多数据源方式介绍 主要方式有以下两种: 通过配置多个SqlSessionFactory 来实现多数据源,这么做的话,未免过于笨重,而且无法实现动态添加数据源... -
SpringBoot整合Mybatis多数据源Druid监控以及atomikos 多数据源事务处理 跨数据源连表查询
2020-03-02 16:16:49SpringBoot整合Mybatis多数据源Druid监控以及atomikos 多数据源事务处理 跨数据源连表查询 -
Mybatis | Mybatis-plus配置多数据源,连接多数据库
2022-01-12 21:45:28使用mybatis或者mybatis-plus配置多数据源,连接多个数据库。实现在同一个项目同一个module里面进行多数据库的操作。 -
Spring Boot HikariCP 一 ——集成多数据源
2017-12-19 20:20:34其实这里介绍的东西主要是参考的另外一篇文章,数据库读写分离的。参考文章就把链接贴出来,里面有那位的...读写分离的功能我已经使用replication集成好了,因为我们需要单独设置每个数据源的链接属性,而且使用的还 -
SpringBoot+JPA+druid动态配置多数据源-从数据库读取连接信息
2019-09-20 16:09:40系统之间的对接,无外乎就是把己方系统的数据发送给第三方或接收第三方系统发送过来的数据(加密签名等机制本文不谈)。作为一个支持动态定义接口的平台,应该有自己的数据库设计,它不应该和业务系统的耦合度太高。... -
baomidou 3.1.0版本的多线程多数据源动态切换的坑
2020-06-16 21:49:50遇到无法切换的问题 原因是具体操作的服务调用的是异步线程 mp的切换数据源信息是放在ThreadLocal中去存储的,如果切换了线程,那么就取不到数据源信息的内容,导致无法切换数据源的问题 对于这种情况,如果异步有... -
springboot使用mybatis多数据源动态切换的实现
2018-01-23 12:39:31需求:项目使用了读写分离,或者数据进行了分库处理,我们希望在操作不同的数据库的时候,我们的程序能够动态的切换到相应的数据库,执行相关的操作。 首先,你需要一个能够正常运行的springboot项目,配置mybatis...