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

前言

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

代码部署

快速新建项目spring-boot项目

1、添加maven依赖

<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.0.1</version>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</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>

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

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

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

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

配置application-test.yml如下

spring:
datasource:
#主库
master:
jdbc-url: jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
#从库
slave:
jdbc-url: jdbc:mysql://127.0.0.1:3306/test2?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
username: root
password: root
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、添加主库配置信息

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

首先看主库配置的代码:

@Configuration
@MapperScan(basePackages = "com.niaobulashi.mapper.master", sqlSessionTemplateRef = "masterSqlSessionTemplate")
public class DataSourceMasterConfig { /**
* 是application-test.yml中的spring.datasource.master配置生效
* @return
*/
@Bean(name = "masterDataSource")
@ConfigurationProperties(prefix = "spring.datasource.master")
@Primary
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
} /**
* 将配置信息注入到SqlSessionFactoryBean中
* @param dataSource 数据库连接信息
* @return
* @throws Exception
*/
@Bean(name = "masterSqlSessionFactory")
@Primary
public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/master/*.xml"));
return bean.getObject();
} /**
* 事务管理器,在实例化时注入主库master
* @param dataSource
* @return
*/
@Bean(name = "masterTransactionManager")
@Primary
public DataSourceTransactionManager masterTransactionManager(@Qualifier("masterDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
} /**
* SqlSessionTemplate具有线程安全性
* @param sqlSessionFactory
* @return
* @throws Exception
*/
@Bean(name = "masterSqlSessionTemplate")
@Primary
public SqlSessionTemplate masterSqlSessionTemplate(@Qualifier("masterSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}

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

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

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

/**
* 将配置信息注入到SqlSessionFactoryBean中
* @param dataSource 数据库连接信息
* @return
* @throws Exception
*/
@Bean(name = "slaveSqlSessionFactory")
public SqlSessionFactory slaveSqlSessionFactory(@Qualifier("slaveDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
// 使配置信息加载到类中,再注入到SqlSessionFactoryBean
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configuration.setMapUnderscoreToCamelCase(true);
bean.setConfiguration(configuration);
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/slave/*.xml"));
return bean.getObject();
}

4、添加从库配置信息

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

代码如下

@Configuration
@MapperScan(basePackages = "com.niaobulashi.mapper.slave", sqlSessionTemplateRef = "slaveSqlSessionTemplate")
public class DataSourceSlaveConfig { /**
* 是application-test.yml中的spring.datasource.master配置生效
* @return
*/
@Bean(name = "slaveDataSource")
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
} /**
* 将配置信息注入到SqlSessionFactoryBean中
* @param dataSource 数据库连接信息
* @return
* @throws Exception
*/
@Bean(name = "slaveSqlSessionFactory")
public SqlSessionFactory slaveSqlSessionFactory(@Qualifier("slaveDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
// 使配置信息加载到类中,再注入到SqlSessionFactoryBean
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configuration.setMapUnderscoreToCamelCase(true);
bean.setConfiguration(configuration);
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/slave/*.xml"));
return bean.getObject();
} /**
* 事务管理器,在实例化时注入主库master
* @param dataSource
* @return
*/
@Bean(name = "slaveTransactionManager")
public DataSourceTransactionManager slaveTransactionManager(@Qualifier("slaveDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
} /**
* SqlSessionTemplate具有线程安全性
* @param sqlSessionFactory
* @return
* @throws Exception
*/
@Bean(name = "slaveSqlSessionTemplate")
public SqlSessionTemplate slaveSqlSessionTemplate(@Qualifier("slaveSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}

5、扩展配置方法会报错

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

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

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

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

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

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

6、数据层代码

代码结构如下

其中SysUserMasterDao代码

public interface SysUserMasterDao {

	/**
* 根据userId查询用户信息
* @param userId 用户ID
*/
List<SysUserEntity> queryUserInfo(Long userId); /**
* 查询所有用户信息
*/
List<SysUserEntity> queryUserAll(); /**
* 根据userId更新用户的邮箱和手机号
* @return
*/
int updateUserInfo(SysUserEntity user); }

7、resource下数据执行语句

SysCodeMasterDao.xml

<mapper namespace="com.niaobulashi.mapper.master.SysUserMasterDao">

    <!--查询所有用户信息-->
<select id="queryUserAll" resultType="com.niaobulashi.entity.SysUserEntity">
SELECT
ur.*
FROM
sys_user ur
WHERE
1 = 1
</select> <!--根据用户userId查询用户信息-->
<select id="queryUserInfo" resultType="com.niaobulashi.entity.SysUserEntity">
SELECT
ur.*
FROM
sys_user ur
WHERE
1 = 1
AND ur.user_id = #{userId}
</select> <!-- 根据UserId,更新邮箱和手机号 -->
<update id="updateUserInfo" parameterType="com.niaobulashi.entity.SysUserEntity">
UPDATE sys_user u
<set>
<if test="email != null">
u.email = #{email},
</if>
<if test="mobile != null">
u.mobile = #{mobile},
</if>
</set>
WHERE
u.user_id = #{userId}
</update> </mapper>

8、Controller层测试

@RestController
public class SysUserController { @Autowired
private SysUserMasterDao sysUserMasterDao; @Autowired
private SysUserSlaveDao sysUserSlaveDao; /**
* 查询所有用户信息Master
* @return
*/
@RequestMapping("/getUserMasterAll")
private List<SysUserEntity> getUserMaster() {
System.out.println("查询主库");
List<SysUserEntity> userList = sysUserMasterDao.queryUserAll();
return userList;
} /**
* 查询所有用户信息Slave
* @return
*/
@RequestMapping("/getUserSlaveAll")
private List<SysUserEntity> getUserSlave() {
System.out.println("查询从库");
List<SysUserEntity> userList = sysUserSlaveDao.queryUserAll();
return userList;
} /**
* 根据userId查询用户信息Master
* @return
*/
@RequestMapping("/getUserMasterById")
private List<SysUserEntity> getUserMasterById(@RequestParam(value = "userId", required = false) Long userId) {
List<SysUserEntity> userList = sysUserMasterDao.queryUserInfo(userId);
return userList;
} /**
* 根据userId查询用户信息Slave
* @return
*/
@RequestMapping("/getUserSlaveById")
private List<SysUserEntity> getUserSlaveById(@RequestParam(value = "userId", required = false) Long userId) {
List<SysUserEntity> userList = sysUserSlaveDao.queryUserInfo(userId);
return userList;
} }

发送查询所有用户接口

主库: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. 使用Toast进行用户提醒(转)

    Toast是Android提供的一个轻量级的用户提醒控件,使用也很简单,就相当一个极简的dialog!!!下面将向您介绍一些Toast的详细用法: 1.普遍使用的方法: Context context ...

  2. 一张图弄明白开源协议-GPL、BSD、MIT、Mozilla、Apache和LGPL 之间的区别

    导读 在开源软件中经常看到各种协议说明,GPL.BSD.MIT.Mozilla.Apache和LGPL. - 这些协议之间的有什么区别 - 如何选择合适的开源协议 请看下文,特作记录一篇,以供后续查看 ...

  3. 王立平--android这四个组成部分

    Android中称为四大组件的为别为:Activity/Service/BroadCast Recevicer/Content  provider Activity:activity是用户和应用程序交 ...

  4. 短暂聊天VS温馨提示

    介绍 作为一个合作格该程序猿,使用的开发工具的熟练使用是我们必须掌握的. 而作为一名优秀的程序猿.我们不仅要能熟练的使用.并且还要去高效的使用. 而作为.Net平台专门定制的开发工具VS那当然是我们必 ...

  5. 图形化界面安装oracle报错Could not execute auto check for display colors using command /usr/bin/xdpyinfo. Check if the DISPLAY variable is set.

    问题描述: 在Linux + oracle 安装时,采有root 帐号登录x-windows 界面,然后 $su oracle 登录录安装Oracle 报以下错误: >>> Coul ...

  6. 【码云周刊第 22 期】GO :互联网时代的 C 语言!

    技术干货 标签:独家译文 1.Go 很好,为什么我们不使用它? 在这篇文章中,我将分享一下为什么我认为它很棒,使用它的一些缺点,以及为什么它还不是我们 Zapier 堆栈中的一部分. 2.从 Scal ...

  7. XF相对布局

    using System; using Xamarin.Forms; using Xamarin.Forms.Xaml; [assembly: XamlCompilation (XamlCompila ...

  8. Socket_Internet 命名空间

    英特网目前有两种地址格式:1.IPv4(32位地址格式)2.IPv6(128位地址格式).IPv4的命名空间为PF_INET,IPv6的命名空间则为PF_INET6. #incldue <sys ...

  9. DataTable转换为Entity(反射&&泛型)

    public static IEnumerable<T> Parse<T>(IEnumerable<DataRow> rows) where T : class, ...

  10. EF 导航属性的使用

    using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threa ...