作为后端开发者,你一定绕不开「增删改查(CRUD)」这个核心工作,尤其是在大型项目中,实体表数量动辄几十上百,关联逻辑复杂,迭代周期长。以前我们用原生 MyBatis 时,总免不了要手写大量 XML SQL 语句,看似灵活,实则在项目规模扩大、团队协作加深后,会暴露出越来越多的痛点。

        而MyBatis-Plus(简称 MP) 作为 MyBatis 的「增强工具」,并非替代 MyBatis,而是在其基础上做了无侵入式扩展,完美解决了手写 XML SQL 的诸多问题,成为大型后端项目中 CRUD 开发的首选。今天我们就来好好聊聊,为什么大型项目更推荐用 MyBatis-Plus,以及它到底能帮我们规避哪些坑。

一、先聊聊:手写 XML SQL 在大型项目中的「致命痛点」

        在小型项目中,手写 XML SQL 可能问题不大,甚至有人觉得「灵活可控」,但在大型项目中,这些「小问题」会被无限放大,成为开发效率、项目维护的绊脚石。

1. 冗余代码泛滥,开发效率极低

        大型项目中,每个实体基本都需要对应的 CRUD 操作,还有分页、条件查询、批量操作等通用需求。如果手写 XML,你需要为每个实体编写:

  • insert(新增单条)、insertBatch(批量新增)
  • deleteById(按 ID 删除)、deleteByCondition(条件删除)
  • updateById(按 ID 更新)、updateByCondition(条件更新)
  • selectById(按 ID 查询)、selectList(查询列表)、selectPage(分页查询)、selectCount(统计数量)

        这些代码大多是「模板化」的,只有表名、字段名不同,却需要重复编写,不仅耗费大量开发时间,还会让 XML 文件变得臃肿不堪 —— 一个大型项目的mapper目录下,XML 文件可能多达上百个,总代码量轻松过万行,后期看着都头疼。

        更关键的是,新人接手项目时,光是要熟悉这些 XML 的写法和规范,就要花费大量时间,严重影响迭代效率。

2. SQL 拼写错误难排查,容易埋下线上隐患

        手写 XML SQL 时,很容易出现各种「低级错误」,而且这些错误在开发环境中可能不会立刻暴露:

  • 字段名拼写错误(比如把user_name写成username,数据库字段是下划线,Java 实体是驼峰,手动映射时容易出错)
  • 语法错误(比如where条件后多写一个逗号、join关联表名写错、limit参数顺序颠倒)
  • 条件拼接错误(比如多条件查询时,漏写and/or,导致查询结果不符合预期)

        这些错误,编译器无法提前校验,只有在运行时才会抛出 SQL 异常,甚至有些隐性错误(比如字段映射错误)不会报错,但会返回错误数据,等到线上发现时,可能已经造成了不可挽回的损失。而且排查这类问题时,需要先把 XML 中的 SQL 复制出来,替换参数后在数据库中执行,繁琐又低效。

3. 通用功能重复实现,团队协作一致性难以保证

        大型项目通常是多人协作开发,而手写 XML SQL 时,每个开发者对通用功能的实现方式可能不一样:

  • 分页查询:有人用limit #{start}, #{size},有人用limit #{size} offset #{start},还有人忘记写count查询,导致分页总数错误
  • 逻辑删除:有人在每个查询中手动加where del_flag = 0,有人在删除时手动改为update del_flag = 1,容易遗漏某些查询场景
  • 排序功能:有人直接拼接order by ${column} ${desc},存在 SQL 注入风险,有人则做了安全处理,写法不统一

        这种「各自为战」的实现方式,不仅增加了代码维护成本,还会导致项目规范混乱,后期要统一优化或修改时,需要逐个修改所有 XML 文件,简直是「灾难级」的工作量。

4. 维护成本居高不下,迭代牵一发而动全身

        大型项目的迭代频率很高,经常会遇到「实体加字段、改字段」的场景。如果手写 XML,你需要同步修改:

  • insert语句中的插入字段列表
  • update语句中的更新字段列表
  • select语句中的查询字段列表
  • 甚至关联的条件查询、分页查询语句

        一旦漏改某个地方,就会出现「字段为 null」「查询不到数据」等问题。而且对于复杂的联表查询 XML,后期开发者可能看不懂前人的逻辑,不敢轻易修改,只能新增一个 XML 方法,导致项目中的 XML 越来越臃肿,维护难度越来越大。

        另外,多人协作时,XML 文件很容易出现「合并冲突」,尤其是在修改同一个实体的 CRUD 语句时,解决冲突的成本也很高。

5. 过多 XML 文件拖慢项目启动速度

        这是大型项目中容易被忽视但影响显著的痛点:MyBatis 启动时,需要扫描项目中所有的 Mapper XML 文件,解析其中的 SQL 语句、ResultMap 映射、参数绑定等配置,生成对应的 Statement 对象和映射元数据。

        大型项目中,XML 文件可能多达几十甚至上百个,每个文件又包含多个 SQL 节点(复杂实体的 XML 可能有 20 + 个 SQL 方法),扫描和解析过程会消耗大量 IO 资源和 CPU 算力:

  • IO 层面:需要逐个读取磁盘上的 XML 文件,文件数量越多,IO 开销越大;
  • 解析层面:MyBatis 需要校验 XML 语法、解析 SQL 结构、绑定实体字段与数据库列,这个过程是串行且耗时的。

        实际项目中,这种开销会直接体现在启动时间上:一个包含 50 个实体、50 个 XML 文件的项目,启动时仅 XML 解析环节就可能增加 3-10 秒耗时,若实体数量达到 100+,启动时间可能从原本的 5 秒拉长到 20 秒以上。这不仅影响本地开发时的重启效率(频繁调试时需反复等待启动),还会拖慢线上部署、容器化启动的速度,影响服务扩容和故障恢复的时效性。

二、MyBatis-Plus:大型项目 CRUD 的「最优解」

        MyBatis-Plus 的核心设计理念是「简化开发、提高效率」,它针对上述手写 XML 的痛点,提供了一整套完善的解决方案,而且完全兼容原生 MyBatis,做到了「无侵入式」增强,让你在享受便捷的同时,不会丢失 MyBatis 的灵活性。

1. 零 XML 实现基础 CRUD,告别冗余模板代码 + 减少启动开销

        MyBatis-Plus 提供了BaseMapper接口和IService接口,内置了大量常用的 CRUD 方法,你只需要让自己的 Mapper 接口继承BaseMapper,让 Service 接口继承IService,即可直接使用这些方法,无需编写一行 XML 代码。

        举个简单的例子,我们先定义一个用户实体类:

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

@Data
@TableName("sys_user") // 映射数据库表名
public class SysUser {
    // 主键ID,配置自增策略
    private Long id;
    // 用户名
    private String userName;
    // 密码
    private String password;
    // 逻辑删除标记(0:未删除,1:已删除)
    private Integer delFlag;
}

然后定义 Mapper 接口,只需继承BaseMapper<SysUser>:

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;

@Repository
public interface SysUserMapper extends BaseMapper<SysUser> {
    // 无需编写任何方法,直接继承BaseMapper的所有CRUD方法
}

接下来,在 Service 中直接注入SysUserMapper,即可调用 CRUD 方法:

import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;

@Service
public class SysUserService {

    @Resource
    private SysUserMapper sysUserMapper;

    // 按ID查询用户
    public SysUser getUserById(Long id) {
        return sysUserMapper.selectById(id);
    }

    // 查询所有未删除的用户
    public List<SysUser> listAllUsers() {
        return sysUserMapper.selectList(null);
    }

    // 新增用户
    public boolean addUser(SysUser sysUser) {
        return sysUserMapper.insert(sysUser) > 0;
    }

    // 按ID删除用户
    public boolean deleteUserById(Long id) {
        return sysUserMapper.deleteById(id) > 0;
    }
}

        看到了吗?无需编写任何 XML,就能实现基础的 CRUD 功能,BaseMapper内置了近 20 个常用方法,覆盖了 90% 的日常 CRUD 场景 —— 这不仅减少了模板代码的编写,更关键的是,大量减少了 XML 文件的数量:原本需要为每个实体创建一个 XML 文件,现在基础 CRUD 场景下无需创建,仅在复杂 SQL 场景下才需要少量 XML,从而大幅降低了项目启动时的扫描和解析开销,让启动速度显著提升。

2. 强大的条件构造器,优雅解决复杂查询,避免 SQL 拼写错误

        对于复杂的条件查询,MyBatis-Plus 提供了QueryWrapper和LambdaQueryWrapper(推荐使用,更安全),支持链式调用,无需手写 SQL,就能拼接各种查询条件、排序、分页等。

        还是以用户查询为例,我们要查询「用户名为「zhangsan」、年龄大于 20、未删除的用户,按创建时间降序排列」,用LambdaQueryWrapper实现:

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import java.util.List;

public List<SysUser> listUsersByCondition() {
    LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
    // 等值查询:user_name = 'zhangsan'
    queryWrapper.eq(SysUser::getUserName, "zhangsan")
            // 大于查询:age > 20
            .gt(SysUser::getAge, 20)
            // 等值查询:del_flag = 0(未删除)
            .eq(SysUser::getDelFlag, 0)
            // 排序:create_time 降序
            .orderByDesc(SysUser::getCreateTime);
    
    return sysUserMapper.selectList(queryWrapper);
}

这种方式有几个核心优势:

  1. 字段名由编译器校验:使用 Lambda 表达式引用实体类的字段,避免了手写字段名的拼写错误,编译器会提前报错,无需等到运行时才发现问题。
  2. 链式调用,可读性强:代码逻辑清晰,一目了然,后期开发者容易理解。
  3. 自动防 SQL 注入:MyBatis-Plus 会自动对参数进行转义处理,避免了手写 SQL 时拼接参数带来的 SQL 注入风险。
  4. 无需手动拼接 and/or:条件构造器会自动处理条件之间的逻辑关系,无需手动添加and/or,减少语法错误。

3. 内置通用功能,统一项目规范,降低团队协作成本

        MyBatis-Plus 内置了大量大型项目所需的通用功能,只需简单配置即可使用,无需重复实现,保证了团队开发的一致性,大大降低了协作成本。

(1)分页查询

        MyBatis-Plus 提供了专门的分页插件,只需配置一次,即可实现全局分页,无需手动编写limit和count语句。

        第一步,配置分页插件(Spring Boot 环境):

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyBatisPlusConfig {
    // 配置分页插件
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 适配MySQL数据库
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

第二步,使用Page对象实现分页查询:

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

public IPage<SysUser> listUsersByPage(Integer pageNum, Integer pageSize) {
    // 构建分页对象:第pageNum页,每页pageSize条数据
    Page<SysUser> page = new Page<>(pageNum, pageSize);
    
    // 构建查询条件
    LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(SysUser::getDelFlag, 0);
    
    // 执行分页查询,自动返回分页结果(包含总条数、总页数、当前页数据)
    return sysUserMapper.selectPage(page, queryWrapper);
}

        分页查询结果会自动包含「总条数、总页数、当前页数据、每页条数」等信息,无需手动查询 count,大大简化了分页开发。

(2)逻辑删除

        大型项目中,很少会直接物理删除数据(删除后无法恢复),通常会采用逻辑删除(通过一个标记字段标记数据是否删除)。MyBatis-Plus 内置了逻辑删除功能,只需简单配置,即可自动实现逻辑删除,无需在每个查询中手动添加del_flag = 0。

        第一步,配置逻辑删除(application.yml):

mybatis-plus:
  global-config:
    db-config:
      # 逻辑删除字段名
      logic-delete-field: delFlag
      # 逻辑未删除值(默认0)
      logic-not-delete-value: 0
      # 逻辑已删除值(默认1)
      logic-delete-value: 1

第二步,在实体类的逻辑删除字段上添加@TableLogic注解:

import com.baomidou.mybatisplus.annotation.TableLogic;
import lombok.Data;

@Data
@TableName("sys_user")
public class SysUser {
    private Long id;
    private String userName;
    private String password;
    
    // 逻辑删除标记
    @TableLogic
    private Integer delFlag;
}

        之后,当你调用sysUserMapper.deleteById(id)时,MyBatis-Plus 会自动将其转换为update sys_user set del_flag = 1 where id = ?,而查询时会自动添加where del_flag = 0的条件,无需手动处理,避免了遗漏。

(3)自动填充

        大型项目中,很多实体都有「创建时间、更新时间、创建人、更新人」等字段,这些字段的值通常是固定的,无需手动设置。MyBatis-Plus 提供了自动填充功能,只需实现MetaObjectHandler接口,即可实现字段的自动填充。

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    // 新增数据时自动填充
    @Override
    public void insertFill(MetaObject metaObject) {
        // 填充创建时间
        this.strictInsertFill(metaObject, "createTime", LocalDateTime::now, LocalDateTime.class);
        // 填充创建人(这里简化处理,实际项目中可从当前登录用户中获取)
        this.strictInsertFill(metaObject, "createBy", () -> "admin", String.class);
    }

    // 更新数据时自动填充
    @Override
    public void updateFill(MetaObject metaObject) {
        // 填充更新时间
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);
        // 填充更新人
        this.strictUpdateFill(metaObject, "updateBy", () -> "admin", String.class);
    }
}

然后在实体类的对应字段上添加@TableField注解,指定填充策略:

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import java.time.LocalDateTime;

@Data
@TableName("sys_user")
public class SysUser {
    private Long id;
    private String userName;
    private String password;
    @TableLogic
    private Integer delFlag;
    
    // 创建时间,新增时自动填充
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    
    // 更新时间,新增和更新时自动填充
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
}

        这样,在新增或更新数据时,这些字段就会自动填充,无需手动设置,减少了重复代码,也保证了字段值的一致性。

4. 低侵入性增强,兼容原生 MyBatis,灵活扩展

        很多人会担心:MyBatis-Plus 虽然便捷,但如果遇到复杂的联表查询、存储过程、自定义 SQL,是不是就无法处理了?(一个合格的产品经理是不会让一个产品出现这种情况的,因为复杂的CRUD操作意味着效率的降速)

        答案是否定的。MyBatis-Plus 是无侵入式的增强工具,完全兼容原生 MyBatis,对于复杂的 SQL 场景,你依然可以采用手写 XML 或注解 SQL 的方式实现,和 MyBatis-Plus 的内置方法共存。

        比如,我们需要做一个复杂的联表查询(用户表关联角色表),可以在SysUserMapper中添加自定义方法,然后编写对应的 XML:

// SysUserMapper.java
public interface SysUserMapper extends BaseMapper<SysUser> {
    // 自定义联表查询方法
    List<SysUserVo> listUserWithRole();
}

然后编写对应的 XML 文件(SysUserMapper.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.example.demo.mapper.SysUserMapper">
    <select id="listUserWithRole" resultType="com.example.demo.vo.SysUserVo">
        SELECT u.id, u.user_name, r.role_name
        FROM sys_user u
        LEFT JOIN sys_user_role ur ON u.id = ur.user_id
        LEFT JOIN sys_role r ON ur.role_id = r.id
        WHERE u.del_flag = 0
    </select>
</mapper>

        这种方式既享受了 MyBatis-Plus 在基础 CRUD 上的便捷,又保留了原生 MyBatis 在复杂 SQL 上的灵活性,完美满足大型项目的多样化需求。

5. 丰富的生态与工具支持,提升项目可维护性

MyBatis-Plus 还提供了大量实用的工具,进一步提升大型项目的开发效率和可维护性:

  • 代码生成器(AutoGenerator):可以根据数据库表自动生成实体类、Mapper、Service、Controller 等代码,减少手动创建的工作量,保证代码规范统一。
  • 性能分析插件:可以监控 SQL 的执行时间,帮助排查慢 SQL,优化项目性能。
  • 乐观锁插件:内置乐观锁实现,无需手动编写版本号控制逻辑,避免并发更新冲突。
  • 完善的文档与社区:MyBatis-Plus 的官方文档详细,社区活跃,遇到问题容易找到解决方案,降低了项目的技术风险。

三、大型项目使用 MyBatis-Plus 的「注意事项」

当然,MyBatis-Plus 也不是「万能的」,在使用过程中,我们需要注意以下几点,避免踩坑:

  1. 复杂联表查询不建议过度依赖条件构造器:虽然条件构造器可以实现简单联表,但对于多表复杂联查、子查询、分组聚合等场景,手写 XML 或注解 SQL 会更清晰,可读性更高,避免条件构造器链式调用过长,难以维护。
  2. 合理使用代码生成器,避免生成冗余代码:代码生成器可以配置生成的内容,比如是否生成 Service、Controller,避免生成不需要的代码,保持项目整洁。
  3. 注意条件构造器的使用规范:优先使用LambdaQueryWrapper,避免使用QueryWrapper手写字段名,减少拼写错误;避免在条件构造器中拼接复杂的 SQL 片段,防止 SQL 注入风险。
  4. 配置统一的全局设置,保持团队规范:比如主键生成策略、逻辑删除字段、自动填充规则等,建议在项目启动时统一配置,避免团队成员各自配置,导致项目规范混乱。

四、总结

        在大型后端项目中,开发效率、项目可维护性、团队协作一致性、启动性能是核心诉求,而 MyBatis-Plus 恰好完美契合了这些诉求。

        它通过「零 XML 实现基础 CRUD」告别了冗余模板代码,减少了 XML 文件数量,从而加快项目启动速度;通过「强大的条件构造器」避免了 SQL 拼写错误;通过「内置通用功能」统一了项目规范;通过「无侵入式增强」保留了原生 MyBatis 的灵活性,成为大型项目 CRUD 开发的「最优解」。

        当然,MyBatis-Plus 不是替代 MyBatis,而是站在 MyBatis 的肩膀上,让我们摆脱了繁琐的 XML 编写,将更多精力放在业务逻辑的实现上(希望每个开发都能遇到一个正常的产品经理)。

本文关键点

  1. 手写 XML SQL 在大型项目中存在冗余代码多、拼写错误难排查、维护成本高、协作一致性差、拖慢启动速度等痛点。
  2. MyBatis-Plus 的核心优势是简化 CRUD 开发、减少 XML 文件(提升启动性能)、提供条件构造器、内置通用功能(分页、逻辑删除等),且兼容原生 MyBatis。
  3. 大型项目使用 MyBatis-Plus 时,需注意复杂联表查询的合理实现,以及团队全局配置的统一。

END

        如果觉得这份基础知识点总结清晰,别忘了动动小手点个赞👍,再关注一下呀~ 后续还会分享更多有关开发问题的干货技巧,同时一起解锁更多好用的功能,少踩坑多提效!🥰 你的支持就是我更新的最大动力,咱们下次分享再见呀~🌟

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐