MyBatis Plus 与 MyBatis的PK:Spring Boot 下的详解、选型与实战

35 阅读7分钟

一、概述

1.1 MyBatis 简介

MyBatis 是一款优秀的半自动化的 ORM 框架,它封装了 JDBC,开发者可以通过 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs 映射成数据库中的记录。它的核心优势在于灵活的 SQL,开发者可以精确地控制每一条 SQL 语句。

1.2 MyBatis Plus 简介

MyBatis Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,旨在简化开发、提高效率。它提供了通用的 CRUD 操作、条件构造器、分页插件等功能,无需编写简单的 XML 和手动设置参数,极大地提升了开发效率。

核心关系:MyBatis Plus 建立在 MyBatis 之上,你可以像使用 MyBatis 一样使用 MP,同时享受它带来的额外便利。


二、核心区别与选型建议

特性MyBatisMyBatis Plus
定位半自动化 ORM 框架,专注于 SQL 本身MyBatis 的增强工具,自动化 CRUD
SQL 控制完全控制,手动编写所有 SQL,灵活性极高大部分通用 SQL 自动生成,特殊 SQL 仍需手动
开发效率较低,需要编写大量模板代码(如基础 CRUD 的 XML)极高,内置通用 Mapper 和 Service,减少大量代码
学习成本较低,主要学习 SQL 映射和动态 SQL在 MyBatis 基础上,需学习条件构造器等新概念
适用场景1. 项目 SQL 极度复杂多变 2. 对数据库操作有高度定制化需求 3. 遗留系统或团队非常熟悉 MyBatis1. 快速开发、业务模型简单的项目(如后台管理系统) 2. 需要快速实现大量标准 CRUD 操作 3. 希望减少模板代码,提升开发效率

选型建议

  • 选择 MyBatis

    • 你的项目涉及大量复杂、高度优化的 SQL,特别是多表关联查询和存储过程调用。
    • 团队对 MyBatis 非常熟悉,且现有项目基于 MyBatis 构建,引入新框架收益不大。
    • 你希望每个 SQL 都经过精心设计和优化,对性能有极致追求。
  • 选择 MyBatis Plus

    • 你需要快速搭建项目,进行敏捷开发。
    • 你的业务中有大量单表的、标准的 CRUD 操作。
    • 你希望代码更加简洁,避免“Simple CRUD”的重复劳动。
    • (推荐) 大部分项目可以以 MyBatis Plus 为基础,对于其无法优雅处理的复杂 SQL,再使用原生 MyBatis 的方式编写。这是一种高效的混合模式。

三、Spring Boot 环境下的实战示例

我们将通过一个 User 实体来演示 MyBatis Plus 的常用功能。

3.1 环境准备

1.添加依赖 (pom.xml)

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <!-- MyBatis Plus 核心依赖 -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.3.1</version> <!-- 请使用最新版本 -->
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

2.配置数据库 (application.yml)

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mp_demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: 123456

# MyBatis Plus 配置
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印 SQL 日志,方便调试
  global-config:
    db-config:
      id-type: auto # 主键策略:数据库自增
      table-prefix: t_ # 表前缀,如果表有前缀如 t_user,这里配置后实体类就不需要带前缀了

3.实体类 (User.java)

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

@Data
@TableName("t_user") // 指定表名,如果表名和类名一致(忽略大小写和下划线)可省略
public class User {
    @TableId(type = IdType.AUTO) // 主键自增
    private Long id;
    private String name;
    private Integer age;
    private String email;
    
    @TableField(fill = FieldFill.INSERT) // 插入时自动填充
    private LocalDateTime createTime;
    
    @TableField(fill = FieldFill.INSERT_UPDATE) // 插入和更新时自动填充
    private LocalDateTime updateTime;
    
    @TableLogic // 逻辑删除注解
    private Integer deleted; // 0-未删除,1-已删除
}

4.Mapper 接口 (UserMapper.java)

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper extends BaseMapper<User> {
    // 继承 BaseMapper 后,无需编写任何方法,即可获得基本的 CRUD 功能
    // 你也可以在这里定义你自己的复杂 SQL 方法
}

3.2 核心功能示例

1. 基础 CRUD

MyBatis Plus 内置了几乎所有单表 CRUD 方法。

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Arrays;
import java.util.List;

@SpringBootTest
public class BasicCRUDTests {

    @Autowired
    private UserMapper userMapper;

    // 插入
    @Test
    public void testInsert() {
        User user = new User();
        user.setName("张三");
        user.setAge(28);
        user.setEmail("zhangsan@example.com");
        int rows = userMapper.insert(user); // INSERT INTO t_user ...
        System.out.println("影响行数: " + rows);
        System.out.println("自动回填的主键ID: " + user.getId());
    }

    // 根据ID查询、更新、删除
    @Test
    public void testSelectUpdateDelete() {
        // 查询
        User user = userMapper.selectById(1L); // SELECT * FROM t_user WHERE id=1
        System.out.println(user);

        // 更新
        user.setAge(30);
        userMapper.updateById(user); // UPDATE t_user SET ... WHERE id=1

        // 删除
        userMapper.deleteById(1L); // DELETE FROM t_user WHERE id=1
    }

    // 批量操作
    @Test
    public void testBatch() {
        // 批量查询
        List<User> users = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L)); // SELECT * FROM t_user WHERE id IN (1,2,3)

        // 批量删除
        userMapper.deleteBatchIds(Arrays.asList(4L, 5L)); // DELETE FROM t_user WHERE id IN (4,5)
    }
}

2. 动态查询(条件构造器 QueryWrapper

这是 MP 最强大的功能之一,可以动态构建 SQL 的 WHERE 条件。

@Test
public void testQueryWrapper() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();

    // 1. 简单条件: name like '%张%' AND age < 30
    queryWrapper.like("name", "张").lt("age", 30);

    // 2. 嵌套条件: (age < 40 AND email IS NOT NULL) OR (name = '李四')
    queryWrapper.and(wq -> wq.lt("age", 40).isNotNull("email"))
                .or(wq -> wq.eq("name", "李四"));

    // 3. 排序、选择特定字段
    queryWrapper.orderByDesc("age") // 按年龄倒序
                .select("id", "name", "age"); // 只查询 id,name,age 字段

    List<User> userList = userMapper.selectList(queryWrapper);
    userList.forEach(System.out::println);

    // 4. 使用 Lambda 表达式(推荐,避免硬编码字段名)
    LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    lambdaQueryWrapper.like(User::getName, "王")
                     .between(User::getAge, 20, 40)
                     .orderByAsc(User::getAge);

    List<User> userList2 = userMapper.selectList(lambdaQueryWrapper);
}

3. 分页查询

需要先配置分页插件

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

@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusPlugin mybatisPlusPlugin() {
        MybatisPlusPlugin plugin = new MybatisPlusPlugin();
        plugin.addInnerPlugin(new PaginationInnerPlugin(DbType.MYSQL)); // 数据库类型
        return plugin;
    }
}

然后使用 Page 对象进行分页查询:

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

@Test
public void testPage() {
    // 参数:当前页,每页大小
    Page<User> page = new Page<>(1, 10); // 查询第1页,每页10条

    LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    lambdaQueryWrapper.ge(User::getAge, 20); // 年龄 >= 20

    // 执行查询,查询结果会自动放入 page 对象中
    Page<User> result = userMapper.selectPage(page, lambdaQueryWrapper);

    System.out.println("总记录数: " + result.getTotal());
    System.out.println("总页数: " + result.getPages());
    System.out.println("当前页数据: " + result.getRecords());
}

4. 批量插入与更新

批量插入:使用 MyBatis Plus 自带的 saveBatch 方法(在 Service 层)。

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;

@Service // 需要定义一个 Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    // 继承了ServiceImpl,已经拥有了saveBatch方法
}

// --- 测试类中 ---
@Autowired
private UserServiceImpl userService;

@Test
public void testBatchInsert() {
    List<User> userList = new ArrayList<>();
    for (int i = 0; i < 5; i++) {
        User user = new User();
        user.setName("BatchUser_" + i);
        user.setAge(20 + i);
        user.setEmail("batch" + i + "@example.com");
        userList.add(user);
    }
    // 批量插入,每批1000条
    boolean isSuccess = userService.saveBatch(userList, 1000); // INSERT INTO t_user (...) VALUES (...), (...), ...
    System.out.println("批量插入是否成功: " + isSuccess);
}

批量更新:同样使用 Service 的 updateBatchById 方法。

@Test
public void testBatchUpdate() {
    List<User> userList = userMapper.selectList(null); // 先查出一些数据
    for (User user : userList) {
        user.setAge(user.getAge() + 1); // 给每个人的年龄+1
    }
    // 根据ID批量更新
    boolean isSuccess = userService.updateBatchById(userList, 1000); // 每批1000条
    System.out.println("批量更新是否成功: " + isSuccess);
}

注意:MP 的批量操作并非真正的 SQL 批量(rewriteBatchedStatements=true 那种),而是将多个操作放在一个 Session 中提交。对于极致性能要求,仍需手动编写 foreach 方式的 SQL。

5. 多表联合查询

这是 MyBatis Plus 的弱项。MP 官方不提供自动化的多表关联查询方案。对于此类需求,最佳实践是回退到原生 MyBatis 的方式

步骤

  1. 在 UserMapper.java 中自定义方法。

  2. 在对应的 XML 文件 (UserMapper.xml) 中编写复杂的联合查询 SQL。

    // 在 UserMapper.java 接口中定义方法
    public interface UserMapper extends BaseMapper<User> {
        // 自定义方法:查询用户及其所属部门信息(假设有部门表 t_dept)
        List<User> selectUserWithDept(@Param("userName") String userName);
    }
    

    <?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.mapper.UserMapper">
    
        <!-- 自定义结果映射 -->
        <resultMap id="UserWithDeptResultMap" type="User" autoMapping="true">
            <id property="id" column="id"/>
            <!-- 映射用户本身字段,autoMapping=true 可自动匹配 -->
            <!-- 关联部门对象 -->
            <association property="dept" javaType="Dept" autoMapping="true">
                <id property="id" column="dept_id"/>
            </association>
        </resultMap>
    
        <!-- 自定义查询 -->
        <select id="selectUserWithDept" resultMap="UserWithDeptResultMap">
            SELECT
                u.*,
                d.id as dept_id,
                d.name as dept_name
            FROM
                t_user u
            LEFT JOIN t_dept d ON u.dept_id = d.id
            <where>
                <if test="userName != null and userName != ''">
                    AND u.name LIKE CONCAT('%', #{userName}, '%')
                </if>
            </where>
        </select>
    </mapper>
    

    这样,就可以在需要复杂查询的地方使用原生 MyBatis 的强大功能,而在单表操作时享受 MyBatis Plus 的便捷。这种混合模式是绝大多数项目的最佳选择。

四、总结

  • MyBatis 是核心,提供了操作数据库的灵活性和控制力。
  • MyBatis Plus 是加速器,在 MyBatis 的基础上,极大地提升了开发简单 CRUD 的效率。

最终建议
对于基于 Spring Boot 的新项目,强烈推荐直接使用 MyBatis Plus。它解决了绝大部分单表操作的问题。当遇到它无法处理的复杂场景(如多表关联、复杂 SQL 优化)时,从容地切换回原生 MyBatis 的编写方式即可。这种组合既能保证开发效率,又能应对复杂需求,是当前 Java Web 开发中数据持久层的最优解之一。