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

一、概述

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,同时享受它带来的额外便利。


二、核心区别与选型建议

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

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)

yaml 复制代码
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)

kotlin 复制代码
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)

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 方法。

java 复制代码
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 条件。

less 复制代码
@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. 分页查询

需要先配置分页插件

kotlin 复制代码
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 对象进行分页查询:

csharp 复制代码
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 层)。

java 复制代码
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 方法。

typescript 复制代码
@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。

    less 复制代码
    // 在 UserMapper.java 接口中定义方法
    public interface UserMapper extends BaseMapper<User> {
        // 自定义方法:查询用户及其所属部门信息(假设有部门表 t_dept)
        List<User> selectUserWithDept(@Param("userName") String userName);
    }
    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.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 开发中数据持久层的最优解之一。

相关推荐
考虑考虑12 小时前
Jpa使用union all
java·spring boot·后端
阿杆1 天前
同事嫌参数校验太丑,我直接掏出了更优雅的 SpEL Validator
java·spring boot·后端
昵称为空C1 天前
SpringBoot3 http接口调用新方式RestClient + @HttpExchange像使用Feign一样调用
spring boot·后端
麦兜*2 天前
MongoDB Atlas 云数据库实战:从零搭建全球多节点集群
java·数据库·spring boot·mongodb·spring·spring cloud
麦兜*2 天前
MongoDB 在物联网(IoT)中的应用:海量时序数据处理方案
java·数据库·spring boot·物联网·mongodb·spring
汤姆yu2 天前
基于springboot的毕业旅游一站式定制系统
spring boot·后端·旅游
计算机毕业设计木哥2 天前
计算机毕设选题推荐:基于Java+SpringBoot物品租赁管理系统【源码+文档+调试】
java·vue.js·spring boot·mysql·spark·毕业设计·课程设计
hdsoft_huge2 天前
Java & Spring Boot常见异常全解析:原因、危害、处理与防范
java·开发语言·spring boot
AD钙奶-lalala2 天前
SpringBoot实现WebSocket服务端
spring boot·后端·websocket
毕设源码-朱学姐2 天前
【开题答辩全过程】以 4S店汽车维修保养管理系统为例,包含答辩的问题和答案
java·spring boot·汽车