cd ..
2025-11-2812 min23 views

MybatisPlus 超详细教程:从入门到复杂查询(含示例代码)

#Spring Boot#MybatisPlus#Data Persistence#Crud Operations#Java

MybatisPlus (MP) 作为 MyBatis 的增强工具,在简化开发、提高效率方面表现出色,已成为 Java 开发中数据持久层框架的首选之一。它在 MyBatis 的基础上只做增强不做改变,完美兼容原生 MyBatis 的所有功能,并提供了更多便捷的特性,如通用 CRUD、条件构造器、代码生成器等。

image.png

本篇博客将带你深入浅出地学习 MybatisPlus,从环境搭建、基本使用到各种复杂查询的实现,并附上详细的示例代码和官方文档链接,助你轻松掌握这个强大的工具。

官方文档是最好的老师,建议收藏: MybatisPlus 官方文档


一、 快速开始:在 Spring Boot 中集成 MybatisPlus

在 Spring Boot 项目中集成 MybatisPlus 非常简单,只需几步即可完成。

1. 添加 Maven 依赖

首先,在你的 pom.xml 文件中添加 MybatisPlus 的启动器依赖。

XML

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.6</version> </dependency>
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

2. 配置 application.yml

接下来,在 application.yml 中配置数据库连接信息。

YAML

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/your_database?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
    username: your_username
    password: your_password
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis-plus:
  # 如果你的 Mapper XML 文件放在 `resources/mapper` 目录下
  mapper-locations: classpath*:/mapper/**/*.xml
  # 实体类所在的包
  type-aliases-package: com.your.project.entity
  # 开启驼峰命名转换
  configuration:
    map-underscore-to-camel-case: true

3. 启动类添加 @MapperScan

在你的 Spring Boot 启动类上添加 @MapperScan 注解,指定 Mapper 接口所在的包路径。

Java

package com.your.project;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.your.project.mapper")
public class YourApplication {
    public static void main(String[] args) {
        SpringApplication.run(YourApplication.class, args);
    }
}

二、 核心功能:通用 CRUD 详解

MybatisPlus 强大的地方在于,你只需要创建一个 Mapper 接口并继承 BaseMapper<T>,就能拥有丰富的内置 CRUD 方法,无需编写任何 SQL。

1. 创建实体类 (Entity)

假设我们有一张 user 表。

SQL

CREATE TABLE user (
  id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
  name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
  age INT NULL DEFAULT NULL COMMENT '年龄',
  email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱'
);

对应的实体类 User.java

Java

package com.your.project.entity;

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

@Data
@TableName("user") // 指定表名
public class User {
    @TableId(type = IdType.AUTO) // 指定主键且自增
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

2. 创建 Mapper 接口

Java

package com.your.project.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.your.project.entity.User;
import org.springframework.stereotype.Repository;

@Repository
public interface UserMapper extends BaseMapper<User> {
}

3. 内置 CRUD 方法示例

现在,你可以直接在 Service 层注入 UserMapper 并调用其方法。

Java

@Autowired
private UserMapper userMapper;

// 插入数据
User user = new User();
user.setName("Gemini");
user.setAge(5);
user.setEmail("gemini@google.com");
userMapper.insert(user); // 返回受影响行数,user对象的id会被自动填充

// 根据 ID 查询
User selectedUser = userMapper.selectById(user.getId());

// 查询所有用户
List<User> userList = userMapper.selectList(null); // 传入 null 查询所有

// 根据 ID 更新
User updateUser = new User();
updateUser.setId(user.getId());
updateUser.setAge(6);
userMapper.updateById(updateUser); // 只更新非空的字段

// 根据 ID 删除
userMapper.deleteById(user.getId());

// 批量删除
userMapper.deleteBatchIds(Arrays.asList(1L, 2L, 3L));

4. Service 层封装 (IService & ServiceImpl)

为了更好地组织业务逻辑和利用 MP 的更多功能(如批量操作),推荐使用 MP 提供的 IServiceServiceImpl

创建 IService 接口:

Java

package com.your.project.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.your.project.entity.User;

public interface IUserService extends IService<User> {
    // 可以在这里定义自己的业务方法
}

创建 ServiceImpl 实现类:

Java

package com.your.project.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.your.project.entity.User;
import com.your.project.mapper.UserMapper;
import com.your.project.service.IUserService;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    // 继承 ServiceImpl 后,同样拥有了丰富的 CRUD 方法
    // 如 save(), updateById(), list(), removeById() 等
}

使用 ServiceImpl 可以让你的代码结构更清晰,并且它内置了对批量操作的优化处理。

三、 强大的条件构造器:Wrapper

当简单的 CRUD 无法满足需求时,就需要使用条件构造器 WrapperWrapper 是 MybatisPlus 的精髓所在,它能让你以面向对象的方式构建复杂的 SQL WHERE 子句,同时保证类型安全。

我们强烈推荐使用 LambdaQueryWrapper,因为它可以利用 Lambda 表达式,避免硬编码字段名,提高代码的可读性和健壮性。

1. 基本查询示例

Java

@Autowired
private IUserService userService;

// 查询年龄大于 20 岁且姓名中包含 "a" 的用户
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.gt(User::getAge, 20)
            .like(User::getName, "a");
List<User> users = userService.list(queryWrapper);

// 查询年龄在 2030 岁之间,且邮箱不为空的用户,按年龄降序排列
LambdaQueryWrapper<User> queryWrapper2 = new LambdaQueryWrapper<>();
queryWrapper2.between(User::getAge, 20, 30)
             .isNotNull(User::getEmail)
             .orderByDesc(User::getAge);
List<User> users2 = userService.list(queryWrapper2);

2. 链式调用

Wrapper 支持链式调用,使代码更加简洁。

Java

List<User> users = userService.lambdaQuery()
    .eq(User::getName, "Sandy")
    .or()
    .lt(User::getAge, 20)
    .list();
// SQL: SELECT * FROM user WHERE (name = 'Sandy' OR age < 20)

3. 动态条件查询

在构建搜索功能时,常常需要根据前端传入的参数动态拼接查询条件。

Java

public List<User> search(String name, Integer startAge, Integer endAge) {
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    // StringUtils.isNotBlank 来自 a`pache commons-lang3`
    wrapper.like(StringUtils.isNotBlank(name), User::getName, name)
           .ge(startAge != null, User::getAge, startAge)
           .le(endAge != null, User::getAge, endAge);
    return userService.list(wrapper);
}

wrapperlike, ge, le 等方法的第一个参数是 boolean 类型,当其为 true 时,该条件才会拼接到 SQL 中。

4. 高级查询

Wrapper 还支持更复杂的查询,如 IN, NOT IN, NESTED (嵌套查询) 等。

Java

// 查询 ID 在 (1, 2, 5) 列表中的用户
userService.lambdaQuery().in(User::getId, Arrays.asList(1L, 2L, 5L)).list();

// 查询 (age > 25 AND name LIKE 'J%') OR (email IS NOT NULL)
userService.lambdaQuery()
    .nested(wq -> wq.gt(User::getAge, 25).likeRight(User::getName, "J"))
    .or()
    .isNotNull(User::getEmail)
    .list();

四、 复杂查询:多表连接与自定义 SQL

虽然 Wrapper 能处理大部分单表查询,但在实际业务中,多表连接查询不可避免。MybatisPlus 推荐通过编写自定义 SQL 的方式来处理复杂的多表查询。

这通常需要结合 MyBatis 原生的 XML 映射文件。

1. 场景设定

假设我们有两张表:user (用户表) 和 orders (订单表),一个用户可以有多个订单。现在需要查询每个用户的基本信息以及他们的订单总数。

2. 创建 DTO (Data Transfer Object)

查询结果包含了多个表的数据,不再适合用单个实体类接收。我们需要创建一个 DTO 来封装查询结果。

Java

package com.your.project.dto;

import lombok.Data;

@Data
public class UserOrderCountDTO {
    private Long userId;
    private String name;
    private String email;
    private Integer orderCount;
}

3. 在 Mapper 中定义方法

UserMapper 接口中添加自定义的查询方法。

Java

package com.your.project.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.your.project.entity.User;
import com.your.project.dto.UserOrderCountDTO;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

@Repository
public interface UserMapper extends BaseMapper<User> {
    /**
     * 查询用户及其订单总数(分页)
     * @param page 分页参数
     * @param name 可选的筛选条件:用户名
     * @return 分页结果
     */
    IPage<UserOrderCountDTO> selectUserWithOrderCount(Page<?> page, @Param("name") String name);
}

4. 编写 Mapper XML 文件

resources/mapper/UserMapper.xml 文件中编写对应的 SQL 语句。

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.your.project.mapper.UserMapper">

    <select id="selectUserWithOrderCount" resultType="com.your.project.dto.UserOrderCountDTO">
        SELECT
            u.id AS userId,
            u.name,
            u.email,
            (SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id) AS orderCount
        FROM
            user u
        <where>
            <if test="name != null and name != ''">
                u.name LIKE CONCAT('%', #{name}, '%')
            </if>
        </where>
        ORDER BY u.id DESC
    </select>

</mapper>

注意:

  • resultType 指向我们创建的 DTO 类。
  • SQL 中的字段名通过 AS 别名与 DTO 中的属性名对应。如果数据库字段是下划线命名法而 DTO 是驼峰命名法(如 user_id -> userId),MP 的驼峰转换配置会自动处理。
  • @Param("name") 注解将方法参数与 XML 中的 #{name} 对应起来。
  • 这里使用了子查询,你也可以使用 LEFT JOINGROUP BY 实现。

5. 分页查询配置与调用

要使用 MybatisPlus 的物理分页功能,需要添加一个配置 Bean。

Java

package com.your.project.config;

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();
        // 添加分页插件,并指定数据库类型
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

在 Service 中调用:

Java

public IPage<UserOrderCountDTO> getUserOrderPage(long current, long size, String name) {
    Page<UserOrderCountDTO> page = new Page<>(current, size);
    return userMapper.selectUserWithOrderCount(page, name);
}

MybatisPlus 的分页插件会自动拦截你的查询,并将其改写为分页 SQL,无需手动编写 LIMIT 子句。

总结

MybatisPlus 以其强大的功能和极低的上手门槛,极大地提升了我们的开发效率。本文从基础的 CRUD 入手,详细介绍了核心的条件构造器 Wrapper 的使用,并演示了如何通过自定义 XML 的方式解决复杂的多表连接查询问题。

掌握 MybatisPlus 的关键在于:

  1. 熟练使用 BaseMapperIService 处理常规的单表操作。
  2. 精通 LambdaQueryWrapper 构建灵活、安全的动态查询条件。
  3. 回归 MyBatis 本质,通过编写 XML SQL 和 DTO 来应对复杂的报表和多表查询需求。

希望这篇详细的教程能帮助你更好地在项目中使用 MybatisPlus,编写出更高效、更优雅的数据访问层代码。

/** Comments(0)*/

Loading comments...