本文在个人技术博客【鸟不拉屎】同步发布,详情可猛戳 亦可扫描文章末尾二维码关注个人公众号【鸟不拉屎】

前言

实际业务场景中,不可能只有一个库,所以就有了分库分表,多数据源的出现。实现了读写分离,主库负责增改删,从库负责查询。这篇文章将实现Spring Boot如何实现多数据源,动态数据源切换,读写分离等操作。

代码部署

快速新建项目spring-boot项目

1、添加maven依赖

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-web</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.mybatis.spring.boot</groupId>
  7. <artifactId>mybatis-spring-boot-starter</artifactId>
  8. <version>2.0.1</version>
  9. </dependency>
  10. <dependency>
  11. <groupId>org.springframework.boot</groupId>
  12. <artifactId>spring-boot-starter-test</artifactId>
  13. <scope>test</scope>
  14. </dependency>
  15. <dependency>
  16. <groupId>org.springframework.boot</groupId>
  17. <artifactId>spring-boot-starter</artifactId>
  18. </dependency>
  19. <dependency>
  20. <groupId>mysql</groupId>
  21. <artifactId>mysql-connector-java</artifactId>
  22. <scope>runtime</scope>
  23. </dependency>
  24. <dependency>
  25. <groupId>org.projectlombok</groupId>
  26. <artifactId>lombok</artifactId>
  27. <optional>true</optional>
  28. </dependency>

2、application配置多数据源读取配置

和之前教程一样,首先配置application.yml

  1. #指定配置文件为test
  2. spring:
  3. profiles:
  4. active: test
  5. #配置Mybatis
  6. mybatis:
  7. configuration:
  8. # 开启驼峰命名转换,如:Table(create_time) -> Entity(createTime)。不需要我们关心怎么进行字段匹配,mybatis会自动识别`大写字母与下划线`
  9. map-underscore-to-camel-case: true
  10. #打印SQL日志
  11. logging:
  12. level:
  13. com.niaobulashi.mapper.*: DEBUG

其中打印SQL日志这块,因为是多数据源,在mapper包下面区分不同的数据库来源xml文件,所以用*表示。

配置application-test.yml如下

  1. spring:
  2. datasource:
  3. #主库
  4. master:
  5. jdbc-url: jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
  6. username: root
  7. password: root
  8. driver-class-name: com.mysql.cj.jdbc.Driver
  9. #从库
  10. slave:
  11. jdbc-url: jdbc:mysql://127.0.0.1:3306/test2?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
  12. username: root
  13. password: root
  14. driver-class-name: com.mysql.cj.jdbc.Driver

从spring.datasource节点开始,区分主库master,从库slave。主库连接的数据库为test,从库连接的数据库为test2。

注意:这里需要注意的是,从Spring Boot2开始,在配置多数据源时有些配置发生了变化,网上许多教程使用的是spring.datasource.url。会出现jdbcUrl is required with driverClassName.的问题。

解决方法:配置多数据源时,将spring.datasource.url配置改为spring.datasource.jdbc-url

3、添加主库配置信息

依据知名博主:纯洁的微笑,写的博文我们来分析一波

首先看主库配置的代码:

  1. @Configuration
  2. @MapperScan(basePackages = "com.niaobulashi.mapper.master", sqlSessionTemplateRef = "masterSqlSessionTemplate")
  3. public class DataSourceMasterConfig {
  4. /**
  5. * 是application-test.yml中的spring.datasource.master配置生效
  6. * @return
  7. */
  8. @Bean(name = "masterDataSource")
  9. @ConfigurationProperties(prefix = "spring.datasource.master")
  10. @Primary
  11. public DataSource masterDataSource() {
  12. return DataSourceBuilder.create().build();
  13. }
  14. /**
  15. * 将配置信息注入到SqlSessionFactoryBean中
  16. * @param dataSource 数据库连接信息
  17. * @return
  18. * @throws Exception
  19. */
  20. @Bean(name = "masterSqlSessionFactory")
  21. @Primary
  22. public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource) throws Exception {
  23. SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
  24. bean.setDataSource(dataSource);
  25. bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/master/*.xml"));
  26. return bean.getObject();
  27. }
  28. /**
  29. * 事务管理器,在实例化时注入主库master
  30. * @param dataSource
  31. * @return
  32. */
  33. @Bean(name = "masterTransactionManager")
  34. @Primary
  35. public DataSourceTransactionManager masterTransactionManager(@Qualifier("masterDataSource") DataSource dataSource) {
  36. return new DataSourceTransactionManager(dataSource);
  37. }
  38. /**
  39. * SqlSessionTemplate具有线程安全性
  40. * @param sqlSessionFactory
  41. * @return
  42. * @throws Exception
  43. */
  44. @Bean(name = "masterSqlSessionTemplate")
  45. @Primary
  46. public SqlSessionTemplate masterSqlSessionTemplate(@Qualifier("masterSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
  47. return new SqlSessionTemplate(sqlSessionFactory);
  48. }
  49. }

问题:看这块masterSqlSessionFactorySqlSessionFactoryBean只获取了spring.datasource.master数据库连接信息,并没有获取多数据库的配置信息mybatis.configuration导致我们需要配置驼峰命名规则,配置信息并没有注入到SqlSessionFactoryBean。这样就导致在查询是,遇到下划线无法解析相应字段user_id,dept_id,create_time

解决方法:在配置中添加Configuration

同时,将配置信息注入到SqlSessionFactoryBean

  1. /**
  2. * 将配置信息注入到SqlSessionFactoryBean中
  3. * @param dataSource 数据库连接信息
  4. * @return
  5. * @throws Exception
  6. */
  7. @Bean(name = "slaveSqlSessionFactory")
  8. public SqlSessionFactory slaveSqlSessionFactory(@Qualifier("slaveDataSource") DataSource dataSource) throws Exception {
  9. SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
  10. // 使配置信息加载到类中,再注入到SqlSessionFactoryBean
  11. org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
  12. configuration.setMapUnderscoreToCamelCase(true);
  13. bean.setConfiguration(configuration);
  14. bean.setDataSource(dataSource);
  15. bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/slave/*.xml"));
  16. return bean.getObject();
  17. }

4、添加从库配置信息

和添加主库配置信息一样,只不过不同的是,不需要添加@Primary首选注解

代码如下

  1. @Configuration
  2. @MapperScan(basePackages = "com.niaobulashi.mapper.slave", sqlSessionTemplateRef = "slaveSqlSessionTemplate")
  3. public class DataSourceSlaveConfig {
  4. /**
  5. * 是application-test.yml中的spring.datasource.master配置生效
  6. * @return
  7. */
  8. @Bean(name = "slaveDataSource")
  9. @ConfigurationProperties(prefix = "spring.datasource.slave")
  10. public DataSource slaveDataSource() {
  11. return DataSourceBuilder.create().build();
  12. }
  13. /**
  14. * 将配置信息注入到SqlSessionFactoryBean中
  15. * @param dataSource 数据库连接信息
  16. * @return
  17. * @throws Exception
  18. */
  19. @Bean(name = "slaveSqlSessionFactory")
  20. public SqlSessionFactory slaveSqlSessionFactory(@Qualifier("slaveDataSource") DataSource dataSource) throws Exception {
  21. SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
  22. // 使配置信息加载到类中,再注入到SqlSessionFactoryBean
  23. org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
  24. configuration.setMapUnderscoreToCamelCase(true);
  25. bean.setConfiguration(configuration);
  26. bean.setDataSource(dataSource);
  27. bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/slave/*.xml"));
  28. return bean.getObject();
  29. }
  30. /**
  31. * 事务管理器,在实例化时注入主库master
  32. * @param dataSource
  33. * @return
  34. */
  35. @Bean(name = "slaveTransactionManager")
  36. public DataSourceTransactionManager slaveTransactionManager(@Qualifier("slaveDataSource") DataSource dataSource) {
  37. return new DataSourceTransactionManager(dataSource);
  38. }
  39. /**
  40. * SqlSessionTemplate具有线程安全性
  41. * @param sqlSessionFactory
  42. * @return
  43. * @throws Exception
  44. */
  45. @Bean(name = "slaveSqlSessionTemplate")
  46. public SqlSessionTemplate slaveSqlSessionTemplate(@Qualifier("slaveSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
  47. return new SqlSessionTemplate(sqlSessionFactory);
  48. }
  49. }

5、扩展配置方法会报错

在网上还看到这样一种配置,单独通过@ConfigurationProperties注解配置Mybatis的配置信息如下

  1. /**
  2. * 试application.yml中的mybatis.configuration配置生效,如果不主动配置,由于@Order配置顺序不同,讲导致配置不能及时生效
  3. * 使配置信息加载到类中,再注入到SqlSessionFactoryBean
  4. * @return
  5. */
  6. @Bean
  7. @ConfigurationProperties(prefix = "mybatis.configuration")
  8. public org.apache.ibatis.session.Configuration configuration() {
  9. return new org.apache.ibatis.session.Configuration();
  10. }

其中prefix,在主库和从库中的id是一样的,必须保持不同,否则idea就会提示报错Duplicate prefix

导致只有主库可以执行Mybatis的配置,从库无效。

  1. @Bean(name = "masterSqlSessionFactory")
  2. @Primary
  3. public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource, org.apache.ibatis.session.Configuration configuration) throws Exception {
  4. SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
  5. // 使配置信息加载到类中,再注入到SqlSessionFactoryBean
  6. bean.setConfiguration(configuration);
  7. bean.setDataSource(dataSource);
  8. bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/master/*.xml"));
  9. return bean.getObject();
  10. }

这块验证只有主库有效,从库的驼峰方法解析无效。后续再来研究下。。。

6、数据层代码

代码结构如下

其中SysUserMasterDao代码

  1. public interface SysUserMasterDao {
  2. /**
  3. * 根据userId查询用户信息
  4. * @param userId 用户ID
  5. */
  6. List<SysUserEntity> queryUserInfo(Long userId);
  7. /**
  8. * 查询所有用户信息
  9. */
  10. List<SysUserEntity> queryUserAll();
  11. /**
  12. * 根据userId更新用户的邮箱和手机号
  13. * @return
  14. */
  15. int updateUserInfo(SysUserEntity user);
  16. }

7、resource下数据执行语句

SysCodeMasterDao.xml

  1. <mapper namespace="com.niaobulashi.mapper.master.SysUserMasterDao">
  2. <!--查询所有用户信息-->
  3. <select id="queryUserAll" resultType="com.niaobulashi.entity.SysUserEntity">
  4. SELECT
  5. ur.*
  6. FROM
  7. sys_user ur
  8. WHERE
  9. 1 = 1
  10. </select>
  11. <!--根据用户userId查询用户信息-->
  12. <select id="queryUserInfo" resultType="com.niaobulashi.entity.SysUserEntity">
  13. SELECT
  14. ur.*
  15. FROM
  16. sys_user ur
  17. WHERE
  18. 1 = 1
  19. AND ur.user_id = #{userId}
  20. </select>
  21. <!-- 根据UserId,更新邮箱和手机号 -->
  22. <update id="updateUserInfo" parameterType="com.niaobulashi.entity.SysUserEntity">
  23. UPDATE sys_user u
  24. <set>
  25. <if test="email != null">
  26. u.email = #{email},
  27. </if>
  28. <if test="mobile != null">
  29. u.mobile = #{mobile},
  30. </if>
  31. </set>
  32. WHERE
  33. u.user_id = #{userId}
  34. </update>
  35. </mapper>

8、Controller层测试

  1. @RestController
  2. public class SysUserController {
  3. @Autowired
  4. private SysUserMasterDao sysUserMasterDao;
  5. @Autowired
  6. private SysUserSlaveDao sysUserSlaveDao;
  7. /**
  8. * 查询所有用户信息Master
  9. * @return
  10. */
  11. @RequestMapping("/getUserMasterAll")
  12. private List<SysUserEntity> getUserMaster() {
  13. System.out.println("查询主库");
  14. List<SysUserEntity> userList = sysUserMasterDao.queryUserAll();
  15. return userList;
  16. }
  17. /**
  18. * 查询所有用户信息Slave
  19. * @return
  20. */
  21. @RequestMapping("/getUserSlaveAll")
  22. private List<SysUserEntity> getUserSlave() {
  23. System.out.println("查询从库");
  24. List<SysUserEntity> userList = sysUserSlaveDao.queryUserAll();
  25. return userList;
  26. }
  27. /**
  28. * 根据userId查询用户信息Master
  29. * @return
  30. */
  31. @RequestMapping("/getUserMasterById")
  32. private List<SysUserEntity> getUserMasterById(@RequestParam(value = "userId", required = false) Long userId) {
  33. List<SysUserEntity> userList = sysUserMasterDao.queryUserInfo(userId);
  34. return userList;
  35. }
  36. /**
  37. * 根据userId查询用户信息Slave
  38. * @return
  39. */
  40. @RequestMapping("/getUserSlaveById")
  41. private List<SysUserEntity> getUserSlaveById(@RequestParam(value = "userId", required = false) Long userId) {
  42. List<SysUserEntity> userList = sysUserSlaveDao.queryUserInfo(userId);
  43. return userList;
  44. }
  45. }

发送查询所有用户接口

主库:http://localhost:8080/getUserMasterAll

从库:http://localhost:8080/getUserSlaveAll

总结

1、通过多数据源方式实现数据库层面的读写分离

2、多数据源链接数据库是,使用spring.datasource.jdbc-url

3、多数据源的mybatis.configuration配置注意需要手动注入SqlSessionFactory

示例代码-github

Spring Boot2(四):使用Spring Boot多数据源实现读写分离的更多相关文章

  1. Spring Boot中整合Sharding-JDBC读写分离示例

    在我<Spring Cloud微服务-全栈技术与案例解析>书中,第18章节分库分表解决方案里有对Sharding-JDBC的使用进行详细的讲解. 之前是通过XML方式来配置数据源,读写分离 ...

  2. 使用Spring配置动态数据源实现读写分离

    最近搭建的一个项目需要实现数据源的读写分离,在这里将代码进行分享,以供参考.关键词:DataSource .AbstractRoutingDataSource.AOP 首先是配置数据源 <!-- ...

  3. 阿里P7教你如何使用 Spring 配置动态数据源实现读写分离

    最近搭建的一个项目需要实现数据源的读写分离,在这里将代码进行分享,以供参考. 关键词:DataSource .AbstractRoutingDataSource.AOP 首先是配置数据源 <!- ...

  4. MyBatis多数据源配置(读写分离)

    原文:http://blog.csdn.net/isea533/article/details/46815385 MyBatis多数据源配置(读写分离) 首先说明,本文的配置使用的最直接的方式,实际用 ...

  5. mybatis用spring的动态数据源实现读写分离

    一.环境: 三个mysql数据库.一个master,两个slaver.master写数据,slaver读数据. 二.原理: 借助Spring的 AbstractRoutingDataSource 这个 ...

  6. Spring Boot2.0使用Spring Security

     一.Spring Secutity简介     Spring 是一个非常流行和成功的 Java 应用开发框架.Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性 ...

  7. Spring Boot + MyBatis + MySQL 实现读写分离

    读写分离要做的事情就是对于一条SQL该选择哪个数据库去执行,至于谁来做选择数据库这件事儿,无非两个,要么中间件帮我们做,要么程序自己做. 读写分离有两种实现方式: 第一种是依靠中间件(比如:MyCat ...

  8. 原理解密 → Spring AOP 实现动态数据源(读写分离),底层原理是什么

    开心一刻 女孩睡醒玩手机,收到男孩发来一条信息:我要去跟我喜欢的人表白了! 女孩的心猛的一痛,回了条信息:去吧,祝你好运! 男孩回了句:但是我没有勇气说不来,怕被打! 女孩:没事的,我相信你!此时女孩 ...

  9. 复习Spring第四课---Spring对国际化的支持

    其实国际化这东西很少使用,之前也就是粗略的学了下,趁今天有空,拿出来稍微写写.以前学android开发的时候,类似于多语言的版本.差别就是一个是手机打开,一个是浏览器打开,本质是一样的. 在Sprin ...

随机推荐

  1. Visual C++文件扩展名解读

    VisualC++文件扩展名解读 [1] .APS:存储二进制资源的资源辅助中间文件(能否加快资源加载速度). [2] .BMP:位图资源文件. [3] .BSC:浏览信息文件.由浏览信息维护工具(B ...

  2. [视频]mac系统下虚拟机parallels安装ubuntu 14.04视频教程

    此文是http://www.mr-wu.cn/install-ubuntu-14-04-on-parallels-for-mac/这篇博文的补充,为整个ubuntu 14.04安装过程的视频录像. m ...

  3. Nginx 设置cors跨域

    在我们的开发中,经常遇到跨域,这个时候,可以通过cors来解决. 解决的方法可以在服务端的代码层或者在web服务器进行设置 在web服务器上进行设置cors 跨域,这样就不必改动代码.以nginx为例 ...

  4. EF相关报错

    EF7无法找寻依赖问题解决方案 现象:使用EF7的过程中,任何"dnx . XXX"的都会报错,提示"cannot resolve dependencies for ta ...

  5. MVC 组件之间的关系

    View和Controller都可以直接请求Model 但是Model不依赖View和controller lController可以直接请求View来显示具体页面 View不依赖Controller ...

  6. Qt中嵌入Directx11(有句柄就可以)

    最近要做个游戏场景编辑器,需要directx11配合gui框架使用,所以简单地弄了一个directx11嵌入到Qt窗体中的程序. 1 建立工程 建一个Qt的工程,配置好directx的包含目录和库目录 ...

  7. 关于"云服务器被检测到对外攻击已阻断该服务器对其它服务器端口的访问"的解决措施

    前段时间阿里云大量发送云服务器对外攻击的信息到邮箱中,邮件信息大概如下: 您的云服务器(XX.XX.XX.XX)由于被检测到对外攻击,已阻断该服务器对其它服务器端口(TCP:XX)的访问,阻断预计将在 ...

  8. wpf的webbrowser与javascript交互

    JS调用C#代码 HTML代码: <button onclick="window.external.Test('called from script code')"> ...

  9. 微信小程序把玩(二十五)loading组件

    原文:微信小程序把玩(二十五)loading组件 loading通常使用在请求网络数据时的一种方式,通过hidden属性设置显示与否 主要属性: wxml <!----> <butt ...

  10. 水晶报表异常“CrystalDecisions.ReportSource.ReportSourceFactory”的类型初始值设定项引发异常,未能加载文件或程序集“log4net

    System.TypeInitializationException: “CrystalDecisions.ReportSource.ReportSourceFactory”的类型初始值设定项引发异常 ...