Spring-多数据源
Spring-多数据源
学习核心
- 多数据源概念和核心
- Spring多数据源整合的两种方式
学习资料
多数据源概念
多数据源概念&引用场景
多数据源概念顾名思义即在一个系统中引用到了不同的数据源。
【场景1】第三方数据交互
常见与外部数据对接方式是通过接口交互的方式来实现功能交互,但是一些场景中有些与第三方对接时,有些合作方并不会为了某些需求而单独开发一个功能,而是通过提供给一个可以访问数据源的只读账号,需要获取什么数据则自行获取并进行业务逻辑处理,这时候就避免不了需要进行多数据源整合了
【场景二】数据整合(数据的分库分表)
当业务数据达到了一个量级,使用单一数据库存储达到了一个瓶颈,需要进行分库分表等操作进行数据管理,在操作数据时,不可避免的涉及到多数据源问题
多数据源整合的方式
【1】使用分包方式,不同的数据源配置不同的MapperScan和mapper文件
【2】使用AOP切片方式,实现动态数据源切换
【3】使用数据库代理中间件,如Mycat等
不同方式的区别
【1】分包方式可以集合JTA(JAVA Transactional API)实现分布式事务,但是整个流程的实现相对来说比较复杂
【2】AOP动态配置数据源方式缺点在于无法实现全局分布式事务,所以如果只是对接第三方数据源,不涉及到需要保证分布式事务的话,可以作为一种选择
【3】使用数据库代理中间件方式是现在比较流行的一种方式,很多公司都是采用这种方式,开发者不需要关注太多与业务无关的问题,把它们都交给数据库代理中间件去处理,大量的通用的数据聚合,事务,数据源切换都由中间件来处理,中间件的性能与处理能力将直接决定应用的读写性能,比较常见的有Mycat、TDDL等,还有阿里出的100%自研的分布式数据库OceanBase,从最底层支持分布式,性能也非常强大
基于分包方式的多数据源
项目构建 & 配置
新建Springboot项目,引入相关maven依赖配置:
- springboot 版本2.7.6
- spring-boot-starter-web
- mybatis-spring-boot-starter 2.3.0
- mysql-connector-java
- lombok
- hutool-all 5.4.5
application.yml配置主从数据源
# 项目启动窗口
server:
port: 8080
# 项目名称
spring:
profiles:
active: dev
application:
name: springboot-mybatis-multiDatasource
datasource:
# 主数据库
master:
# 注意,整合多数据源时如果使用springboot默认的数据库连接池Hikari,指定连接数据使用的是jdbc-url而不是url属性
jdbc-url: jdbc:mysql://localhost:3306/db_noob_demo?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
username: db_noob_demo
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
# 副数据库
slave:
# 注意,整合多数据源时如果使用springboot默认的数据库连接池Hikari,指定连接数据使用的是jdbc-url而不是url属性
jdbc-url: jdbc:mysql://localhost:3306/db_noob_demo_bak?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
username: db_noob_demo_bak
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
主从数据源配置(config)
@Configuration
// 指定主数据库扫描对应的Mapper文件,生成代理对象
@MapperScan(basePackages ="com.noob.framework.mapper.master" ,sqlSessionFactoryRef = "masterSqlSessionFactory")
public class MasterDataSourceConfig {
// mapper.xml所在地址
private static final String MAPPER_LOCATION = "classpath*:mapper/master/*.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注解作用同上
*/
@Primary
@Bean(name = "masterTransactionManager")
public PlatformTransactionManager dataSourceTransactionManager(@Qualifier("masterDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
/**
* session工厂,Primary注解作用同上
*/
@Primary
@Bean(name = "masterSqlSessionFactory")
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();
}
}
@Configuration
// 指定从数据库扫描对应的Mapper文件,生成代理对象
@MapperScan(basePackages = "com.noob.framework.mapper.salve", sqlSessionFactoryRef = "slaveSqlSessionFactory")
public class SlaveDataSourceConfig {
// mapper.xml所在地址
private static final String MAPPER_LOCATION = "classpath*:mapper/salve/*.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();
}
}
mapper层构建(其中mapper层分包存储)
// 主Mapper
package com.noob.framework.mapper.master;
@Mapper
public interface MasterUserMapper {
public List<User> getAllUser();
}
// 主Mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.noob.framework.mapper.master.MasterUserMapper">
<select id="getAllUser" resultType="com.noob.framework.model.User">
select * from user
</select>
</mapper>
// 从Mapper
package com.noob.framework.mapper.salve;
@Mapper
public interface SalveUserMapper {
public List<User> getAllUser();
}
// 从Mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.noob.framework.mapper.salve.SalveUserMapper">
<select id="getAllUser" resultType="com.noob.framework.model.User">
select * from user
</select>
</mapper>
service层构建(此处为了简化,只构建一个service用于测试)
public interface UserService {
public List<User> getMasterUserList();
public List<User> getSalveUserList();
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private MasterUserMapper masterUserMapper;
@Autowired
private SalveUserMapper salveUserMapper;
@Override
public List<User> getMasterUserList() {
return masterUserMapper.getAllUser();
}
@Override
public List<User> getSalveUserList() {
return salveUserMapper.getAllUser();
}
}
controller层构建
@RequestMapping("/user")
@Controller
public class UserController {
private static final Logger log = LoggerFactory.getLogger(UserController.class);
@Autowired
private UserService userService;
@RequestMapping("/list")
public void getAllUser(){
// 分别调用不同数据源获取信息
List<User> masterUserList = userService.getMasterUserList();
log.info("主数据源:{}" , masterUserList.toString());
List<User> salveUserList = userService.getSalveUserList();
log.info("副数据源:{}" , salveUserList.toString());
}
}
启动访问测试
访问URL
http://localhost:8080/user/list
// output 查看控制台
[nio-8080-exec-4] c.n.framework.controller.UserController : 主数据源:[User(userId=44, userName=小龙)]
[nio-8080-exec-4] c.n.framework.controller.UserController : 副数据源:[User(userId=44, userName=夏红), User(userId=45, userName=张宝)]