MyBatis-Plus通用Service

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

相关推荐
CoderYanger1 小时前
A.每日一题——1523. 在区间范围内统计奇数数目
java·数据结构·算法·leetcode·职场和发展
程序员-周李斌1 小时前
ArrayBlockingQueue 源码解析
java·开发语言·后端·哈希算法·散列表
该用户已不存在1 小时前
6款Vibe Coding工具,让开发从从容容游刃有余
后端·aigc·ai编程
编程修仙2 小时前
第一篇 认识SpringBoot
java·spring boot
qwepoilkjasd2 小时前
std::string详解
后端
bcbnb2 小时前
iOS 应用上架流程的工程化拆解 从签名体系到提交审核的全过程管控
后端
数新网络2 小时前
Compaction in Apache Iceberg
后端
骇客野人2 小时前
.gitignore文件常用设置
java
神奇的程序员2 小时前
实现一个内网服务监测告警系统
后端·自动化运维