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 开发中数据持久层的最优解之一。

相关推荐
一叶飘零_sweeeet38 分钟前
在分布式环境下正确使用MyBatis二级缓存
java·分布式·mybatis
一枚小小程序员哈1 小时前
基于Android的车位预售预租APP/基于Android的车位租赁系统APP/基于Android的车位管理系统APP
android·spring boot·后端·struts·spring·java-ee·maven
用户30742971671581 小时前
Spring AI实战:基于ElevenLabs 实现文本转语音的实时音频流
java·spring boot·ai编程
Java水解2 小时前
Spring Boot 事务详解
spring boot·后端
Tom·Ge3 小时前
Spring AI 入门指南:三步将AI集成到Spring Boot应用
人工智能·spring boot·spring
tingting01193 小时前
Spring Boot 外部配置指定不生效的原因与解决
java·spring boot·后端
亲爱的非洲野猪3 小时前
Spring Boot 与传统 Spring:从 WAR 到可执行 JAR,颠覆性的部署哲学
spring boot·spring·jar
2501_909686704 小时前
基于SpringBoot的网上点餐系统
java·spring boot·后端
neoooo4 小时前
Spring Boot 3 + Kafka 实战指南
java·spring boot·kafka