MyBatis-Plus通用Service
什么是MyBatis-Plus的通用Service
MyBatis-Plus 的通用 Service 是对 BaseMapper 通用接口的上层增强,旨在解决 Service 层重复编写单表 CRUD、批量操作、事务处理等通用逻辑的问题,提供标准化、可复用的封装体系,让开发者无需从零编写 Service 层基础代码,聚焦核心业务逻辑。
其中 MyBatis-Plus 对 BaseMapper 通用接口的封装内容在我的上一篇博客里有详细介绍 MyBatis-Plus基本CRUD
MyBatis-Plus的通用Service编写步骤
通用 Service 的编写分为定义 Service 接口和实现 Service 类两步,核心是继承 MP 提供的通用接口 / 实现类,同时可扩展自定义业务方法。
首先是定义 Service 接口
MyBatis-Plus 的通用 Service 核心接口为 IService<T>,该接口定义了涵盖CRUD、批量操作、分页查询等维度的通用方法,所有自定义的通用Service接口均需继承该接口,泛型指定实体类。
java
public interface UserService extends IService<User> {
}
其次是编写 Service 实现类
IService<T> 接口的默认实现类为 ServiceImpl<M extends BaseMapper<T>, T>,实现类需继承该默认实现类并实现自定义的 Service 接口,其中泛型参数M为继承自BaseMapper的具体Mapper接口类型、T为实体类类型。
java
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService{
}
MyBatis-Plus的通用Service和BaseMapper的关系
BaseMapper<T> 作为MP在数据访问层的通用封装,直接对接数据库,核心职责是封装单表基础CRUD操作的SQL执行逻辑;而 IService<T> 是MP在业务逻辑层的通用封装,核心是基于BaseMapper<T> 进一步封装更贴合业务场景的通用方法。
ServiceImpl<M extends BaseMapper<T>, T> 类是连接 IService<T> 与 BaseMapper<T> 的核心桥梁:一方面通过Spring容器自动注入泛型指定的 BaseMapper<T> 实现类实例,将Service层的通用操作委托给Mapper层的 BaseMapper<T> 执行;另一方面在委托过程中完成批量处理、事务管理、返回值适配等业务层专属的增强逻辑。
增强逻辑包含:复用基础 CRUD 并将返回值封装为更贴合业务的 boolean 类型,还补充了批量新增 / 更新 / 删除、链式查询 / 更新、计数 / 数据存在性判断、分页查询等增强能力,且批量操作默认支持事务,更适配业务逻辑层的开发需求。
举例测试
插入一条记录

该函数通过调用 BaseMapper<T> 中定义的操作数据库的 insert 方法,进行插入一条记录
查询总记录数

其中 Wrappers.emptyWrapper() 会创建一个无任何查询条件的 Wrapper 对象,MP 解析这个空 Wrapper 时,不会生成 SQL 的 WHERE 子句
java
@SpringBootTest
public class MybatisTestService {
@Autowired
private UserService userService;
@Test
public void testGetCount() {
long count = userService.count();
System.out.println("返回的记录数:" + count);
}
}
测试结果

测试批量插入函数
批量插入逻辑设计在通用Service层而非通用Mapper层,核心是为了规避数据库的底层技术限制,同时契合分层架构的职责定位,具体原因如下:
数据库对单条 SQL 长度有严格限制,海量数据下拼接的单条 SQL 会超出长度阈值,触发 PacketTooBigException 等异常,单条超长 SQL 的解析、执行耗时极长,易导致数据库连接超时、锁等待,甚至引发数据库性能雪崩。
而 Service 层作为业务逻辑层,核心职责是统筹业务流程、管控执行逻辑,能够有效突破上述限制,MP 的 saveBatch 底层是先将待插入的海量数据拆分为固定批次,再逐批次调用Mapper层执行单批插入SQL,最后通过批量提交机制完成整体插入。通过拆分成多条 SQL、控制批次大小规避长度限制,适配海量数据插入场景。

指定默认每1000条为一批

其中 default 是 Java 8+ 接口默认方法关键字,允许接口自带方法体,实现类可直接复用该逻辑,也可按需重写;
@Transactional 注解用于声明该方法运行在 Spring 事务中,是批量插入原子性的核心保障,而 rollbackFor = Exception.class 用于扩展回滚范围,任何 Exception 类型的异常都会触发事务回滚。
下面使用两种方式测试该方法
java
@SpringBootTest
public class MybatisTestService {
@Autowired
private UserService userService;
@Test
public void testSaveBatch(){
ArrayList<User> users = new ArrayList<>();
for (int i = 0; i < 5; i++) {
User user = new User();
user.setName("szy" + i);
user.setAge(20 + i);
users.add(user);
}
userService.saveBatch(users);
users.forEach(System.out::println);
}
@Transactional
@Test
@Rollback(false)
public void testSaveBatch1(){
ArrayList<User> users = new ArrayList<>();
for (int i = 0; i < 5; i++) {
User user = new User();
user.setName("szy" + i);
user.setAge(20 + i);
users.add(user);
}
userService.saveBatch(users);
users.forEach(System.out::println);
}
}
测试结果:


具体解释:
testSaveBatch 函数调用 userService.saveBatch(users) 时,Spring 检测到 saveBatch 函数有 @Transactional(rollbackFor = Exception.class) 注解,会为 saveBatch 新建一个独立的业务事务。若无异常,业务事务正常提交,数据写入数据库,若有异常则触发业务事务回滚,数据不写入。
testSaveBatch1 函数上添加了 @Transactional 注解,Spring Test 在测试方法执行前会开启事务,测试方法执行完后不管成功与失败都会自动回滚这个事务(默认 @Rollback(true)),添加 @Rollback(false) 注解,关闭默认事务回滚,测试方法执行成功后,事务不会回滚而是正常提交。
需要注意的是 @Rollback 只对测试方法上开启的事务生效,如果测试方法没加 @Transactional,@Rollback(false) 无效。
测试自定义批次的批量插入

该方法中没有被 default 修饰,属于未实现的抽象方法,所有实现该接口的类必须强制重写这个方法,否则编译不通过。
在 ServiceImpl 中对该方法的重写如下:

测试该函数
java
/**
* 自定义批次的批量插入
*/
@Test
public void testSaveBatch2(){
ArrayList<User> users = new ArrayList<>();
for (int i = 0; i < 5; i++) {
User user = new User();
user.setName("szy" + i);
user.setAge(20 + i);
users.add(user);
}
userService.saveBatch(users,5);
users.forEach(System.out::println);
}
手动指定批次大小为 5
