随着项目规模的扩大,单一数据源已无法满足复杂业务需求,多数据源(动态数据源)应运而生。本文将介绍两种 MyBatis-Plus 的多数据源扩展插件:开源生态的 dynamic-datasource 和 企业级生态的 mybatis-mate。
dynamic-datasource 是一个开源的 Spring Boot 多数据源启动器,提供了丰富的功能,包括数据源分组、敏感信息加密、独立初始化表结构等。
数据源分组:适用于多种场景,如读写分离、一主多从等。 敏感信息加密:使用 ENC() 加密数据库配置信息。 独立初始化:支持每个数据库独立初始化表结构和数据库。 自定义注解:支持自定义注解,需继承 DS。 简化集成:提供对 Druid、HikariCP 等连接池的快速集成。 组件集成:支持 Mybatis-Plus、Quartz 等组件的集成方案。 动态数据源:支持项目启动后动态增加或移除数据源。 分布式事务:提供基于 Seata 的分布式事务方案。
本框架专注于数据源切换,不限制具体操作。 配置文件中以下划线 _ 分割的数据源首部为组名。 切换数据源可以是组名或具体数据源名。 默认数据源名为 master,可通过 spring.datasource.dynamic.primary 修改。 方法上的注解优先于类上的注解。
MyBatis-Plus 提供了一套强大的条件构造器(Wrapper),用于构建复杂的数据库查询条件。Wrapper 类允许开发者以链式调用的方式构造查询条件,无需编写繁琐的 SQL 语句,从而提高开发效率并减少 SQL 注入的风险。
在 MyBatis-Plus 中,Wrapper 类是构建查询和更新条件的核心工具。以下是主要的 Wrapper 类及其功能:
AbstractWrapper:这是一个抽象基类,提供了所有 Wrapper 类共有的方法和属性。它定义了条件构造的基本逻辑,包括字段(column)、值(value)、操作符(condition)等。所有的 QueryWrapper、UpdateWrapper、LambdaQueryWrapper 和 LambdaUpdateWrapper 都继承自 AbstractWrapper。
QueryWrapper:专门用于构造查询条件,支持基本的等于、不等于、大于、小于等各种常见操作。它允许你以链式调用的方式添加多个查询条件,并且可以组合使用 and 和 or 逻辑。
UpdateWrapper:用于构造更新条件,可以在更新数据时指定条件。与 QueryWrapper 类似,它也支持链式调用和逻辑组合。使用 UpdateWrapper 可以在不创建实体对象的情况下,直接设置更新字段和条件。
LambdaQueryWrapper:这是一个基于 Lambda 表达式的查询条件构造器,它通过 Lambda 表达式来引用实体类的属性,从而避免了硬编码字段名。这种方式提高了代码的可读性和可维护性,尤其是在字段名可能发生变化的情况下。
LambdaUpdateWrapper:类似于 LambdaQueryWrapper,LambdaUpdateWrapper 是基于 Lambda 表达式的更新条件构造器。它允许你使用 Lambda 表达式来指定更新字段和条件,同样避免了硬编码字段名的问题。
如果被调用服务方法有HttpServletResponse,会导致调用失败,因为Feign不能处理HttpServletResponse
使用返回值ResponseEntity<org.springframework.core.io.Resource>进行处理
java @GetMapping("/download")
public void download(@RequestParam(value = "fileUrl") String fileUrl, HttpServletResponse response){
try {
ResponseEntity<org.springframework.core.io.Resource> responseEntity = remoteFileService.downloadByRes(fileUrl);;
if (responseEntity.getStatusCode().is2xxSuccessful()) {
org.springframework.core.io.Resource resource = responseEntity.getBody();
if (resource != null) {
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=" + resource.getFilename());
try (OutputStream outputStream = response.getOutputStream()) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = resource.getInputStream().read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.flush();
}
}
} else {
response.setStatus(responseEntity.getStatusCodeValue());
}
} catch (IOException e) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
Seata 是阿里巴巴开源的分布式事务中间件,以高效并且对业务 0 侵入的方式,解决微服务场景下面临的分布式事务问题。 Seata 将为用户提供了 AT、TCC、SAGA 和XA 事务模式,为用户打造一站式的分布式解决方案。AT模式是阿里首推的模式,阿里云上有商用版本的GTS(Global Transaction Service 全局事务服务)。
在 AT 模式下,用户只需关注自己的“业务 SQL”,用户的 “业务 SQL” 作为一阶段,Seata 框架会自动生成事务的二阶段提交和回滚操作。
Server 端存储模式(store.mode)支持三种方式:
file:单机模式(默认为此模式),全局事务会话信息存储在内存中,读写并持久化至本地文件 root.data (bin\sessionStore\root.data) 中,性能较高。 db:高可用模式(Mysql 5.7+),全局事务会话信息通过db共享,相应性能差些。 redis:Seata-Server 1.3及以上版本支持,性能较高,但存在事务信息丢失风险,请提前配置适合当前场景的redis持久化配置。
Seata AT 模式 需要使用到 undo_log 表。
sql-- 注意此处0.3.0+ 增加唯一索引 ux_undo_log
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
`ext` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
IService 是 MyBatis-Plus 提供的一个通用 Service 层接口,它封装了常见的 CRUD 操作,包括插入、删除、查询和分页等。通过继承 IService 接口,可以快速实现对数据库的基本操作,同时保持代码的简洁性和可维护性。
IService 接口中的方法命名遵循了一定的规范,如 get 用于查询单行,remove 用于删除,list 用于查询集合,page 用于分页查询,这样可以避免与 Mapper 层的方法混淆。