全部版块 我的主页
论坛 经济学论坛 三区 教育经济学
281 0
2025-11-16

目录

MyBatis-Plus简介

什么是MyBatis-Plus

MyBatis-Plus(简称MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,旨在简化开发、提升效率。

核心特性

  • 无侵入:仅做增强,不改变现有工程
  • 低损耗:启动时自动注入基础CURD,性能几乎无损,直接面向对象操作
  • 强大的CRUD操作:内置通用Mapper和服务,少量配置即可实现单表大部分CRUD操作
  • 支持Lambda调用:通过Lambda表达式,便捷地编写各类查询条件
  • 支持主键自动生成:支持4种主键策略,可自由配置
  • 支持ActiveRecord模式:支持ActiveRecord形式调用,实体类只需继承Model类即可进行强大的CRUD操作
  • 支持自定义全局操作:支持全局方法注入
  • 内置代码生成器:可通过代码或Maven插件快速生成Mapper、Model、Service、Controller层代码
  • 内置分页插件:基于MyBatis物理分页,开发者无需关注具体操作
  • 分页插件支持多种数据库:支持MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、PostgreSQL、SQLServer等多种数据库
  • 内置性能分析插件:可输出SQL语句及其执行时间
  • 内置全局拦截插件:提供全表删除、更新操作的智能分析阻断

MyBatis-Plus vs MyBatis

环境准备

系统要求

  • JDK 8 或更高版本
  • Maven 3.3 或更高版本
  • MySQL 5.7 或更高版本

创建Spring Boot项目

        <?xml version="1.0" encoding="UTF-8"?>
        <project xmlns="http://maven.apache.org/POM/4.0.0"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                 http://maven.apache.org/xsd/maven-4.0.0.xsd">
            <modelVersion>4.0.0</modelVersion>
            <groupId>com.example</groupId>
            <artifactId>mybatis-plus-tutorial</artifactId>
            <version>1.0-SNAPSHOT</version>
            <packaging>jar</packaging>
            <parent>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-parent</artifactId>
                <version>2.7.14</version>
                <relativePath/>
            </parent>
            <properties>
                <java.version>8</java.version>
                <mybatis-plus.version>3.5.3.1</mybatis-plus.version>
            </properties>
            <dependencies>
                <!-- Spring Boot Web -->
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-web</artifactId>
                </dependency>
                <!-- MyBatis-Plus -->
                <dependency>
                    <groupId>com.baomidou</groupId>
                    <artifactId>mybatis-plus-boot-starter</artifactId>
                    <version>${mybatis-plus.version}</version>
                </dependency>
    
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.18</version>
</dependency>
<!-- 代码生成器 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.3.1</version>
</dependency>
<!-- 模板引擎 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
数据库准备
-- 创建数据库
CREATE DATABASE mybatis_plus_tutorial DEFAULT CHARSET utf8mb4;
-- 使用数据库
USE mybatis_plus_tutorial;
-- 创建用户表
CREATE TABLE user (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
age INT,
email VARCHAR(100) NOT NULL,
manager_id BIGINT,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
version INT DEFAULT 0,
deleted TINYINT DEFAULT 0,
INDEX idx_manager_id (manager_id)
);
-- 插入测试数据
INSERT INTO user (name, age, email, manager_id) VALUES
('张三', 25, 'zhangsan@example.com', NULL),
('李四', 30, 'lisi@example.com', 1);

('王五', 28, 'wangwu@example.com', 1),
('赵六', 32, 'zhaoliu@example.com', 1),
('孙七', 26, 'sunqi@example.com', 2);

-- 建立商品表
CREATE TABLE product (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
price DECIMAL(10,2) NOT NULL,
category_id BIGINT,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted TINYINT DEFAULT 0
);

-- 建立订单表
CREATE TABLE orders (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
total_amount DECIMAL(10,2) NOT NULL,
status VARCHAR(20) DEFAULT 'PENDING',
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted TINYINT DEFAULT 0
);

-- 建立订单明细表
CREATE TABLE order_item (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
order_id BIGINT NOT NULL,
product_id BIGINT NOT NULL,
quantity INT NOT NULL,
price DECIMAL(10,2) NOT NULL,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

快速入门指南

  1. 配置文件
    创建 application.yml:
    server:
    port: 8080
    spring:
    datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_plus_tutorial?useSSL=false&serverTimezone=UTC&characterEncoding=UTF-8
    username: root
    password: 123456
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
    initial-size: 5
    min-idle: 5
    max-active: 20
    test-while-idle: true
    validation-query: SELECT 1
    # MyBatis-Plus 设置
    mybatis-plus:
    configuration:
    # 记录
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 下划线转驼峰
    map-underscore-to-camel-case: true
    global-config:
    db-config:
    # 自动主键策略
    id-type: auto
    # 逻辑删除字段
    logic-delete-field: deleted
    # 逻辑删除标记(已删除)
    logic-delete-value: 1
    # 逻辑删除标记(未删除)
    logic-not-delete-value: 0
    # 字段填充规则
    insert-strategy: not_null
    update-strategy: not_null
    select-strategy: not_null
    # Mapper XML 文件位置
    mapper-locations: classpath*:mapper/**/*.xml
    # 实体类包路径
    type-aliases-package: com.example.entity
    # 记录设置
    logging:
    level:
    com.example.mapper: debug
    root: info
  2. 实体类
    创建 src/main/java/com/example/entity/User.java:
    package com.example.entity;
    import com.baomidou.mybatisplus.annotation.*;
    import lombok.Data;
    import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("user")
public class User {
    /**
     * 主键ID,自动递增
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    /**
     * 名称
     */
    @TableField("name")
    private String name;
    /**
     * 年纪
     */
    @TableField("age")
    private Integer age;
    /**
     * 电子邮箱
     */
    @TableField("email")
    private String email;
    /**
     * 管理者ID
     */
    @TableField("manager_id")
    private Long managerId;
    /**
     * 创建日期,自动填充
     */
    @TableField(value = "create_time", fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    /**
     * 更新日期,自动填充
     */
    @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    /**
     * 版本号,用于乐观锁定
     */
    @Version
    @TableField("version")
    private Integer version;
    /**
     * 逻辑删除标记
     */
    @TableLogic
    @TableField("deleted")
    private Integer deleted;
}
3. Mapper接口
创建src/main/java/com/example/mapper/UserMapper.java:
package com.example.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.example.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface UserMapper extends BaseMapper {
    /**
     * 定制查询方法
     */
    @Select("SELECT * FROM user WHERE age > #{age}")
    List selectByAgeGreaterThan(@Param("age") Integer age);
    /**
     * 定制分页查询
     */
    IPage selectUserPage(Page page, @Param("age") Integer age);
}
4. Service层
创建src/main/java/com/example/service/UserService.java:
package com.example.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.entity.User;
public interface UserService extends IService {
    /**
     * 按年龄查找用户
     */
    List getUsersByAge(Integer age);
    /**
     * 批量保存用户
     */
    boolean saveBatch(List users);
}
创建src/main/java/com/example/service/impl/UserServiceImpl.java:
package com.example.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.entity.User;
import com.example.mapper.UserMapper;
import com.example.service.UserService;
import org.springframework.stereotype.Service;
import java.util.List
@Service
public class UserServiceImpl extends ServiceImpl implements UserService {
    @Override
    public List getUsersByAge(Integer age) {
        QueryWrapper queryWrapper = new QueryWrapper<>();
        queryWrapper.gt("age", age);
        return this.list(queryWrapper);
    }
    @Override
    public boolean saveBatch(List users) {
        // 批量保存,自动管理批次尺寸
        return this.saveBatch(users, 1000);
    }
}
5. 控制器层
创建src/main/java/com/example/controller/UserController.java:
package com.example.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.entity.User;
import com.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List
@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserService userService;
    /**
     * 查询全部用户
     */
    @GetMapping
    public List getAllUsers() {
        return userService.list();
    }
    /**
     * 按ID查询用户
     */
    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        return userService.getById(id);
    }
    /**
     * 分页查询用户
     */
    @GetMapping("/page")
    public IPage getUsersPage(@RequestParam(defaultValue = "1") Integer current,
                                    @RequestParam(defaultValue = "10") Integer size) {
        Page page = new Page<>(current, size);
        return userService.page(page);
    }
    /**
     * 条件查询用户
     */
    @GetMapping("/search")
    public List searchUsers(@RequestParam(required = false) String name,
                                  @RequestParam(required = false) Integer minAge) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
if (name != null) {
    queryWrapper.like("name", name);
}
if (minAge != null) {
    queryWrapper.ge("age", minAge);
}
return userService.list(queryWrapper);
}
/**
 * 添加用户
 */
@PostMapping
public boolean addUser(@RequestBody User user) {
    return userService.save(user);
}
/**
 * 修改用户
 */
@PutMapping("/{id}")
public boolean updateUser(@PathVariable Long id, @RequestBody User user) {
    user.setId(id);
    return userService.updateById(user);
}
/**
 * 移除用户
 */
@DeleteMapping("/{id}")
public boolean deleteUser(@PathVariable Long id) {
    return userService.removeById(id);
}
}
6. 启动文件
创建src/main/java/com/example/Application.java:
package com.example;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.example.mapper")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
7. 字段填充设定
创建src/main/java/com/example/config/MyMetaObjectHandler.java:
package com.example.config;
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.class, LocalDateTime.now());
        this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }
    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }
}
核心特性
注释解析
@TableName
// 定义表名称
@TableName("sys_user")
public class User {
    // ...
}
// 应用共享前缀
@TableName(value = "user", schema = "mybatis_plus")
public class User {
    // ...
}
// 忽略字段
@TableName(value = "user", excludeProperty = {"password"})
public class User {
    // ...
}
@TableId
    
public class User {
// 自增主键
@TableId(type = IdType.AUTO)
private Long id;
// 雪花算法ID
@TableId(type = IdType.ASSIGN_ID)
private Long id;
// UUID
@TableId(type = IdType.ASSIGN_UUID)
private String id;
// 手动输入
@TableId(type = IdType.INPUT)
private Long id;
// 无状态(跟随全局)
@TableId(type = IdType.NONE)
private Long id;
}

@TableField
public class User {
// 指定字段名称
@TableField("user_name")
private String name;
// 字段不用于查询
@TableField(select = false)
private String password;
// 字段不在表中存在
@TableField(exist = false)
private String remark;
// 字段填充策略
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
// 构造条件时忽略
@TableField(condition = SqlCondition.LIKE)
private String name;
}

@Version
public class User {
// 乐观锁版本号
@Version
private Integer version;
}

@TableLogic
public class User {
// 逻辑删除字段
@TableLogic
private Integer deleted;
// 自定义逻辑删除值
@TableLogic(value = "0", delval = "1")
private Integer deleted;
}

主键策略详解
public enum IdType {
AUTO(0),        // 数据库自动增长
NONE(1),        // 无状态,遵循全局
INPUT(2),       // 用户手动输入
ASSIGN_ID(3),   // 分配ID(主键类型为数字(Long和Integer)或字符串)(自3.3.0起), 使用接口IdentifierGenerator的方法nextId
ASSIGN_UUID(4); // 分配UUID, 主键类型为字符串(自3.3.0起), 使用接口IdentifierGenerator的方法nextUUID
}

全局配置
@Configuration
public class MybatisPlusConfig {
@Bean
public GlobalConfig globalConfig() {
GlobalConfig globalConfig = new GlobalConfig();
// 数据库配置
GlobalConfig.DbConfig dbConfig = new GlobalConfig.DbConfig();
dbConfig.setIdType(IdType.AUTO);
dbConfig.setTablePrefix("t_");
dbConfig.setLogicDeleteField("deleted");
dbConfig.setLogicDeleteValue("1");
dbConfig.setLogicNotDeleteValue("0");
globalConfig.setDbConfig(dbConfig);
return globalConfig;
}
}

CRUD接口
BaseMapper接口
MyBatis-Plus提供了BaseMapper接口,包含了一系列常用的CRUD操作方法:
public interface BaseMapper<T> extends Mapper<T> {
// 插入数据
int insert(T entity);
// 根据ID删除记录
int deleteById(Serializable id);
// 根据条件删除记录
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
// 批量删除记录

int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);

// 按照ID进行修改

int updateById(@Param(Constants.ENTITY) T entity);

// 根据条件进行修改

int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);

// 按照ID进行查询

T selectById(Serializable id);

// 批量查询

List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);

// 按照条件进行查询

List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

// 分页查询

IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

// 统计总数

Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

}

IService接口

MyBatis-Plus提供了IService接口,封装了更多的业务操作:

// 保存

boolean save(T entity);

boolean saveBatch(Collection<T> entityList);

boolean saveBatch(Collection<T> entityList, int batchSize);

// 保存或更新

boolean saveOrUpdate(T entity);

boolean saveOrUpdateBatch(Collection<T> entityList);

// 删除

boolean removeById(Serializable id);

boolean removeByIds(Collection<? extends Serializable> idList);

boolean remove(Wrapper<T> queryWrapper);

// 更新

boolean updateById(T entity);

boolean updateBatchById(Collection<T> entityList);

boolean update(Wrapper<T> updateWrapper);

boolean update(T entity, Wrapper<T> updateWrapper);

// 查询

T getById(Serializable id);

List<T> listByIds(Collection<? extends Serializable> idList);

List<T> list();

List<T> list(Wrapper<T> queryWrapper);

IPage<T> page(IPage<T> page);

IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);

// 统计

int count();

int count(Wrapper<T> queryWrapper);

使用示例

@Service

public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

public void crudExample() {

// 1. 保存

User user = new User();

user.setName("张三");

user.setAge(25);

user.setEmail("zhangsan@example.com");

this.save(user); // 自动填充创建时间

// 2. 批量保存

List<User> userList = Arrays.asList(
new User().setName("李四").setAge(30).setEmail("lisi@example.com"),
new User().setName("王五").setAge(28).setEmail("wangwu@example.com")
);
this.saveBatch(userList);
// 3. 修改
User updateUser = new User();
updateUser.setId(1L);
updateUser.setAge(26);
this.updateById(updateUser); // 仅修改非空字段
// 4. 检索
User queryUser = this.getById(1L);
List<User> allUsers = this.list();
// 5. 条件检索
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name", "张")
.ge("age", 20)
.orderByDesc("create_time");
List<User> users = this.list(queryWrapper);
// 6. 分页检索
Page<User> page = new Page<>(1, 10);
IPage<User> userPage = this.page(page, queryWrapper);
// 7. 移除
this.removeById(1L);
this.removeByIds(Arrays.asList(2L, 3L));
// 8. 计数
int count = this.count();
int adultCount = this.count(new QueryWrapper<User>().ge("age", 18));
}
}
条件构造工具
QueryWrapper
用于构建查询条件:
@Test
public void testQueryWrapper() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 1. 基础条件
queryWrapper.eq("name", "张三")           // name = '张三'
.ne("age", 25)               // age != 25
.gt("age", 20)               // age > 20
.ge("age", 20)               // age >= 20
.lt("age", 30)               // age < 30
.le("age", 30)               // age <= 30
.between("age", 20, 30)      // age BETWEEN 20 AND 30
.notBetween("age", 20, 30)   // age NOT BETWEEN 20 AND 30
.like("name", "张")          // name LIKE '%张%'
.notLike("name", "张")       // name NOT LIKE '%张%'
.likeLeft("name", "张")      // name LIKE '%张'
.likeRight("name", "张")     // name LIKE '张%'
.isNull("manager_id")        // manager_id IS NULL
.isNotNull("manager_id")     // manager_id IS NOT NULL
.in("age", Arrays.asList(20, 25, 30))      // age IN (20,25,30)
.notIn("age", Arrays.asList(20, 25, 30))   // age NOT IN (20,25,30)
.inSql("id", "select id from user where age > 25")  // id IN (select id from user where age > 25)
    

.notInSql("id", "select id from user where age > 25"); // id NOT IN (select id from user where age > 25)

// 2. 逻辑条件

queryWrapper.and(wrapper -> wrapper.eq("name", "张三").or().eq("name", "李四")) // (name = '张三' OR name = '李四')

.or(wrapper -> wrapper.gt("age", 30).lt("age", 40)); // OR (age > 30 AND age < 40)

// 3. 排序

queryWrapper.orderByAsc("age") // ORDER BY age ASC

.orderByDesc("create_time") // ORDER BY create_time DESC

.orderBy(true, true, "name"); // ORDER BY name ASC

// 4. 分组

queryWrapper.groupBy("age", "name") // GROUP BY age, name

.having("count(*) > {0}", 2); // HAVING count(*) > 2

// 5. 字段选择

queryWrapper.select("id", "name", "age") // SELECT id, name, age

.select(User.class, info -> !info.getColumn().equals("create_time")); // 排除create_time字段

List<User> users = userService.list(queryWrapper);

}

UpdateWrapper

用于构建更新条件:

@Test

public void testUpdateWrapper() {

UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();

// 设置更新字段和条件

updateWrapper.set("age", 26) // SET age = 26

.set("email", "new@example.com") // SET email = 'new@example.com'

.eq("name", "张三") // WHERE name = '张三'

.gt("age", 20); // AND age > 20

userService.update(updateWrapper);

// 或者使用实体+条件

User user = new User();

user.setAge(27);

UpdateWrapper<User> wrapper = new UpdateWrapper<>();

wrapper.eq("name", "张三");

userService.update(user, wrapper);

}

LambdaQueryWrapper

类型安全的条件构造器:

@Test

public void testLambdaQueryWrapper() {

LambdaQueryWrapper<User> lambdaQuery = new LambdaQueryWrapper<>();

lambdaQuery.eq(User::getName, "张三")

.ge(User::getAge, 20)

.like(User::getEmail, "@example.com")

.orderByDesc(User::getCreateTime);

List<User> users = userService.list(lambdaQuery);

// 简化写法

List<User> users2 = userService.lambdaQuery()

.eq(User::getName, "张三")

.ge(User::getAge, 20)

.list();

}

LambdaUpdateWrapper

类型安全的更新构造器:

@Test

public void testLambdaUpdateWrapper() {

        LambdaUpdateWrapper<User> lambdaUpdate = new LambdaUpdateWrapper<>();
        lambdaUpdate.set(User::getAge, 26)
            .set(User::getEmail, "new@example.com")
            .eq(User::getName, "张三");
        userService.update(lambdaUpdate);
        // 简化写法
        userService.lambdaUpdate()
            .set(User::getAge, 27)
            .eq(User::getName, "张三")
            .update();
    

条件构造器高级用法

        @Test
        public void testAdvancedWrapper() {
            // 1. 动态条件
            String name = "张三";
            Integer minAge = 20;
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(StringUtils.isNotBlank(name), User::getName, name)
                .ge(minAge != null, User::getAge, minAge);
            // 2. 函数条件
            wrapper.apply("date_format(create_time, '%Y-%m-%d') = '2023-01-01'")
                .apply("FIND_IN_SET({0}, name)", "张,李");
            // 3. 子查询
            wrapper.inSql(User::getId, "select user_id from orders where total_amount > 1000")
                .exists("select 1 from orders o where o.user_id = user.id");
            // 4. 嵌套条件
            wrapper.and(w -> w.eq(User::getName, "张三").or().eq(User::getName, "李四"))
                .or(w -> w.gt(User::getAge, 30).isNotNull(User::getManagerId));
            List<User> users = userService.list(wrapper);
        }
    

分页插件

配置分页插件

        @Configuration
        @MapperScan("com.example.mapper")
        public class MybatisPlusConfig {
            @Bean
            public MybatisPlusInterceptor mybatisPlusInterceptor() {
                MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
                // 分页插件
                PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
                // 设置单页最大限制数量,默认500,-1不受限制
                paginationInterceptor.setMaxLimit(1000L);
                // 溢出总页数后是否进行处理
                paginationInterceptor.setOverflow(false);
                // 生成countSql优化掉join
                paginationInterceptor.setOptimizeJoin(true);
                interceptor.addInnerInterceptor(paginationInterceptor);
                return interceptor;
            }
        }
    

基本分页使用

        @Test
        public void testPage() {
            // 1. 基本分页
            Page<User> page = new Page<>(1, 5); // 第1页,每页5条
            IPage<User> userPage = userService.page(page);
            System.out.println("总记录数:" + userPage.getTotal());
            System.out.println("总页数:" + userPage.getPages());
            System.out.println("当前页:" + userPage.getCurrent());
            System.out.println("每页大小:" + userPage.getSize());
        }
    
        System.out.println("数据:" + userPage.getRecords());
        // 2. 条件分页
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.ge("age", 20).orderByDesc("create_time");
        Page<User> conditionPage = new Page<>(1, 5);
        IPage<User> result = userService.page(conditionPage, queryWrapper);
        // 3. 不查询总数(适用于大数据量场景)
        Page<User> pageWithoutCount = new Page<>(1, 5, false);
        IPage<User> resultWithoutCount = userService.page(pageWithoutCount);
    

自定义分页查询

在Mapper中自定义分页方法:

        @Mapper
        public interface UserMapper extends BaseMapper<User> {
            /**
             * 自定义分页查询
             * 第一个参数必须是Page类型
             */
            IPage<User> selectUserPage(Page<User> page, @Param("age") Integer age);
            /**
             * 自定义分页查询返回自定义VO
             */
            IPage<UserVO> selectUserVOPage(Page<UserVO> page, @Param("age") Integer age);
        }
    

创建对应的XML文件src/main/resources/mapper/UserMapper.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">
            <select id="selectUserPage" resultType="com.example.entity.User">
                SELECT * FROM user
                WHERE age > #{age}
                ORDER BY create_time DESC
            </select>
            <select id="selectUserVOPage" resultType="com.example.vo.UserVO">
                SELECT
                    u.id,
                    u.name,
                    u.age,
                    u.email,
                    m.name as managerName
                FROM user u
                LEFT JOIN user m ON u.manager_id = m.id
                WHERE u.age > #{age}
                ORDER BY u.create_time DESC
            </select>
        </mapper>
    

分页VO类

        package com.example.vo;
        import lombok.Data;
        @Data
        public class UserVO {
            private Long id;
            private String name;
            private Integer age;
            private String email;
            private String managerName;
        }
    

分页使用示例

        @Test
        public void testCustomPage() {
            // 自定义分页查询
            Page<User> page = new Page<>(1, 5);
            IPage<User> userPage = userMapper.selectUserPage(page, 20);
            // 自定义VO分页查询
            Page<UserVO> voPage = new Page<>(1, 5);
            IPage<UserVO> userVOPage = userMapper.selectUserVOPage(voPage, 20);
        }
    

分页优化

        @Configuration
        public class MybatisPlusConfig {
            @Bean
    
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
    // 针对大容量数据的优化
    paginationInterceptor.setOptimizeJoin(true);
    // 定制count查询
    paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize() {
        @Override
        public String optimize(String countSql) {
            // 定制count SQL优化逻辑
            return super.optimize(countSql);
        }
    });
    interceptor.addInnerInterceptor(paginationInterceptor);
    return interceptor;
}

代码生成器

基础代码生成器

package com.example.generator;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableFill;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import java.util.Arrays;

public class CodeGenerator {
    public static void main(String[] args) {
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();
        // 1. 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("generator");
        gc.setOpen(false); // 生成后是否开启输出目录
        gc.setFileOverride(false); // 是否替代现有文件
        gc.setServiceName("%sService"); // service 命名方式,%s为占位符
        gc.setServiceImplName("%sServiceImpl"); // service impl 命名方式
        gc.setMapperName("%sMapper"); // mapper 命名方式
        gc.setXmlName("%sMapper"); // mapper xml 命名方式
        gc.setControllerName("%sController"); // controller 命名方式
        gc.setDateType(DateType.TIME_PACK); // 使用java.time包
        gc.setSwagger2(true); // 实体属性 Swagger2 注解
        mpg.setGlobalConfig(gc);
        // 2. 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/mybatis_plus_tutorial?useSSL=false&serverTimezone=UTC");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("123456");
mpg.setDataSource(dsc);
// 3. 包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName("system"); // 模块名称
pc.setParent("com.example"); // 父包名称
pc.setEntity("entity");
pc.setMapper("mapper");
pc.setService("service");
pc.setServiceImpl("service.impl");
pc.setController("controller");
mpg.setPackageInfo(pc);
// 4. 定制配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// 无需操作
}
};
// 定制输出配置
List<FileOutConfig> focList = new ArrayList<>();
// 定制配置将被优先输出
focList.add(new FileOutConfig("/templates/mapper.xml.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
// 定制输出文件名称,如果 Entity 设定了前后缀,请留意此处 xml 名称的变化!
return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
+ "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 5. 模板配置
TemplateConfig templateConfig = new TemplateConfig();
templateConfig.setXml(null); // 配置定制输出模板,模板类别:entity, service, controller, mapper, xml
mpg.setTemplate(templateConfig);
// 6. 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel); // 数据库表映射至实体的命名策略
strategy.setColumnNaming(NamingStrategy.underline_to_camel); // 数据库表字段映射至实体的命名策略
strategy.setEntityLombokModel(true); // 启用lombok
strategy.setRestControllerStyle(true); // RESTful API风格控制器
strategy.setInclude("user", "product", "orders", "order_item"); // 需要生成的表名称
strategy.setControllerMappingHyphenStyle(true); // 驼峰转连字符
strategy.setTablePrefix("t_"); // 表前缀
// 字段填充设定
strategy.setTableFillList(Arrays.asList(
new TableFill("create_time", FieldFill.INSERT),
new TableFill("update_time", FieldFill.INSERT_UPDATE)
));
// 乐观锁字段设定
strategy.setVersionFieldName("version");
// 逻辑删除字段设定
strategy.setLogicDeleteFieldName("deleted");
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new VelocityTemplateEngine());
mpg.execute();
}
}
高级代码生成器配置
package com.example.generator;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.Collections;

public class AdvancedCodeGenerator {
    public static void main(String[] args) {
        FastAutoGenerator.create("jdbc:mysql://localhost:3306/mybatis_plus_tutorial", "root", "123456")
            .globalConfig(builder -> {
                builder.author("generator") // 设定作者
                    .enableSwagger() // 启用 Swagger 模式
                    .fileOverride() // 替换已生成的文件
                    .outputDir(System.getProperty("user.dir") + "/src/main/java"); // 指定输出目录
            })
            .packageConfig(builder -> {
                builder.parent("com.example") // 设定父包名
                    .moduleName("system") // 设定父包模块名
                    .pathInfo(Collections.singletonMap(OutputFile.xml,
                        System.getProperty("user.dir") + "/src/main/resources/mapper")); // 设定 mapperXml 生成路径
            })
            .strategyConfig(builder -> {
                builder.addInclude("user", "product") // 设定需要生成的表名
                    .addTablePrefix("t_", "c_") // 设定过滤表前缀
                    // 实体策略配置
                    .entityBuilder()
                        .enableLombok() // 启用 Lombok 模型
                        .enableTableFieldAnnotation() // 启用生成实体时生成字段注解
                        .enableActiveRecord() // 启用 ActiveRecord 模式
                        .versionColumnName("version") // 乐观锁字段名(数据库)
                        .versionPropertyName("version") // 乐观锁属性名(实体)
                        .logicDeleteColumnName("deleted") // 逻辑删除字段名(数据库)
                        .logicDeletePropertyName("deleted") // 逻辑删除属性名(实体)
                        .naming(NamingStrategy.underline_to_camel) // 数据库表映射到实体的命名策略
                        .columnNaming(NamingStrategy.underline_to_camel) // 数据库表字段映射到实体的命名策略
                        .addTableFills(new Column("create_time", FieldFill.INSERT))
                        .addTableFills(new Property("updateTime", FieldFill.INSERT_UPDATE))
                    // 控制器策略配置
                    .controllerBuilder()
                        .enableRestStyle() // 启用生成 @RestController 控制器
                        .enableHyphenStyle() // 启用驼峰转连字符
                    // 服务策略配置
                    .serviceBuilder()
                        .formatServiceFileName("%sService") // 格式化 service 接口文件名称
                        .formatServiceImplFileName("%sServiceImpl") // 格式化 service 实现类文件名称
                    // 映射器策略配置
                    .mapperBuilder()
                        .enableMapperAnnotation() // 启用 @Mapper 注解
                        .enableBaseResultMap() // 启用 BaseResultMap 生成
                        .enableBaseColumnList(); // 启用 BaseColumnList
            })
            .templateEngine(new FreemarkerTemplateEngine()) // 使用 Freemarker 引擎模板,默认是 Velocity 引擎模板
            .execute();
    }
}

自定义模板

在src/main/resources/templates/目录下创建自定义模板:

entity.java.vm:

package ${package.Entity};
#foreach($pkg in ${table.importPackages})
import ${pkg};
#end
#if(${swagger2})
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
#end
#if(${entityLombokModel})
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
#end
/**
* <p>
* ${table.comment!}
* </p>
*
* @author ${author}
* @since ${date}
*/
#if(${entityLombokModel})
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
#end
#if(${table.convert})
@TableName("${table.name}")
#end
#if(${swagger2})
@ApiModel(value="${entity}对象", description="${table.comment!}")
#end
#if(${superEntityClass})
public class ${entity} extends ${superEntityClass}#if(${activeRecord})<${entity}>#end {
#elseif(${activeRecord})
public class ${entity} extends Model<${entity}> {
#else
public class ${entity} implements Serializable {
#end
private static final long serialVersionUID = 1L;
## ----------  BEGIN 字段循环遍历  ----------
#foreach($field in ${table.fields})
#if(${field.keyFlag})
#set($keyPropertyName=${field.propertyName})
#end
#if("$!field.comment" != "")
#if(${swagger2})
@ApiModelProperty(value = "${field.comment}")
#else
/**
* ${field.comment}
*/
#end
#end
#if(${field.keyFlag})
## 主键
#if(${field.keyIdentityFlag})
@TableId(value = "${field.name}", type = IdType.AUTO)
#elseif(!$null.isNull(${idType}) && "$!idType" != "")
@TableId(value = "${field.name}", type = IdType.${idType})
#elseif(${field.convert})
@TableId("${field.name}")
#end
## 普通字段
#elseif(${field.fill})
## -----   存在字段填充配置   -----
#if(${field.convert})
@TableField(value = "${field.name}", fill = FieldFill.${field.fill})
#else
@TableField(fill = FieldFill.${field.fill})
#end
#elseif(${field.convert})
@TableField("${field.name}")
#end
## 乐观锁标注
#if(${versionFieldName}==${field.name})
@Version
#end
## 逻辑删除标注
#if(${logicDeleteFieldName}==${field.name})
@TableLogic
#end
private ${field.propertyType} ${field.propertyName};
#end
## ----------  END 字段循环遍历  ----------
#if(!${entityLombokModel})
#foreach($field in ${table.fields})
#if(${field.propertyType.equals("boolean")})
#set($getprefix="is")
#else
#set($getprefix="get")
#end
public ${field.propertyType} ${getprefix}${field.capitalName}() {
return ${field.propertyName};
}
#if(${entityBuilderModel})
public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
#else
public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
#end
this.${field.propertyName} = ${field.propertyName};
#if(${entityBuilderModel})
return this;
#end
}
#end
#end
#if(${entityColumnConstant})
#foreach($field in ${table.fields})
public static final String ${field.name.toUpperCase()} = "${field.name}";
#end
#end
#if(${activeRecord})
@Override
protected Serializable pkVal() {
#if(${keyPropertyName})
return this.${keyPropertyName};
#else
return null;
#end
}
#end
#if(!${entityLombokModel})
@Override
public String toString() {
return "${entity}{ " +
#foreach($field in ${table.fields})
#if($!{foreach.index} == 0)
"${field.propertyName}=" + ${field.propertyName} +
#else
", ${field.propertyName}=" + ${field.propertyName} +
#end
#end
"}";
}
#end
}

多租户配置

@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 租户插件
TenantLineInnerInterceptor tenantInterceptor = new TenantLineInnerInterceptor();
tenantInterceptor.setTenantLineHandler(new TenantLineHandler() {
@Override
public Expression getTenantId() {
// 从环境上下文中获取租户标识
Long tenantId = TenantContextHolder.getTenantId();
return new LongValue(tenantId);
}
@Override
public String getTenantIdColumn() {
return "tenant_id"; // 租户列名
}
@Override
public boolean ignoreTable(String tableName) {
// 排除多租户影响的表
return "tenant".equals(tableName) || "user".equals(tableName);
}
});
interceptor.addInnerInterceptor(tenantInterceptor);
return interceptor;
}
}

租户上下文

public class TenantContextHolder {
private static final ThreadLocal<Long> TENANT_ID = new ThreadLocal<>();
public static void setTenantId(Long tenantId) {
    TENANT_ID.set(tenantId);
}
public static Long getTenantId() {
    return TENANT_ID.get();
}
public static void clear() {
    TENANT_ID.remove();
}
}

租户拦截器
@Component
public class TenantInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {
        String tenantId = request.getHeader("Tenant-Id");
        if (StringUtils.isNotEmpty(tenantId)) {
            TenantContextHolder.setTenantId(Long.parseLong(tenantId));
        }
        return true;
    }
    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler,
                                Exception ex) throws Exception {
        TenantContextHolder.clear();
    }
}

数据权限
数据权限拦截器
@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 数据权限插件
        DataPermissionInterceptor dataPermissionInterceptor = new DataPermissionInterceptor();
        dataPermissionInterceptor.setDataPermissionHandler(new DataPermissionHandler() {
            @Override
            public Expression getSqlSegment(Expression where, String mappedStatementId) {
                // 获取当前用户信息
                UserInfo currentUser = getCurrentUser();
                if (currentUser.isAdmin()) {
                    // 管理员可以查看所有数据
                    return where;
                }
                // 普通用户只能查看自己的数据
                Expression userCondition = new EqualsTo(new Column("user_id"), new LongValue(currentUser.getId()));
                if (where == null) {
                    return userCondition;
                } else {
                    return new AndExpression(where, userCondition);
                }
            }
        });
        interceptor.addInnerInterceptor(dataPermissionInterceptor);
        return interceptor;
    }
    private UserInfo getCurrentUser() {
        // 从安全上下文获取当前用户
        return SecurityContextHolder.getCurrentUser();
    }
}

性能分析
性能分析插件
@Configuration
@Profile({"dev", "test"}) // 只在开发和测试环境启用
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 性能分析插件
        PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
performanceInterceptor.setMaxTime(1000); // 设定SQL执行最长时间,超时则中断
performanceInterceptor.setFormat(true); // 整理SQL格式
performanceInterceptor.setWriteInLog(true); // 记录到日志
interceptor.addInnerInterceptor(performanceInterceptor);
return interceptor;
}
}
SQL执行分析
@Component
@Slf4j
public class SqlExecutionAnalyzer implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
    long startTime = System.currentTimeMillis();
    try {
        Object result = invocation.proceed();
        return result;
    } finally {
        long endTime = System.currentTimeMillis();
        long executionTime = endTime - startTime;
        if (executionTime > 1000) {
            MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
            BoundSql boundSql = ms.getBoundSql(invocation.getArgs()[1]);
            log.warn("慢SQL检测 - {}ms - {}", executionTime, boundSql.getSql());
        }
    }
}
@Override
public Object plugin(Object target) {
    return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
    // 配置属性
}
}
最佳实践
1. 项目架构
src/
├── main/
│   ├── java/
│   │   └── com/example/
│   │       ├── config/        # 配置类
│   │       ├── controller/    # 控制器
│   │       ├── entity/        # 实体类
│   │       ├── mapper/        # Mapper接口
│   │       ├── service/       # 服务层
│   │       │   └── impl/      # 服务实现
│   │       ├── vo/            # 视图对象
│   │       ├── dto/           # 数据传输对象
│   │       └── util/          # 工具类
│   └── resources/
│       ├── mapper/            # Mapper XML文件
│       └── application.yml    # 配置文件
2. 实体类设计准则
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("sys_user")
@ApiModel(value = "用户对象", description = "系统用户表")
public class User extends BaseEntity {
@ApiModelProperty(value = "用户ID")
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@ApiModelProperty(value = "用户名")
@TableField("username")
private String username;
@ApiModelProperty(value = "密码")
@TableField(value = "password", select = false)
private String password;
@ApiModelProperty(value = "邮箱")
@TableField("email")
private String email;
    
@ApiModelProperty(value = "电话号码")
@TableField("phone")
private String phone;
@ApiModelProperty(value = "状态:0-禁用,1-启用")
@TableField("status")
private Integer status;
@ApiModelProperty(value = "部门标识")
@TableField("dept_id")
private Long deptId;
@ApiModelProperty(value = "角色集合")
@TableField(exist = false)
private List<Role> roles;
}
3. Service层设计规范
// 接口定义
public interface UserService extends IService<User> {
/**
* 分页检索用户列表
*/
IPage<UserVO> pageUsers(UserQueryDTO queryDTO);
/**
* 按用户名检索用户
*/
User getByUsername(String username);
/**
* 重置用户密码
*/
boolean resetPassword(Long userId, String newPassword);
/**
* 更新用户状态
*/
boolean updateStatus(Long userId, Integer status);
}
// 实现类
@Service
@Transactional(rollbackFor = Exception.class)
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
@Transactional(readOnly = true)
public IPage<UserVO> pageUsers(UserQueryDTO queryDTO) {
Page<User> page = new Page<>(queryDTO.getCurrent(), queryDTO.getSize());
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.like(StringUtils.isNotBlank(queryDTO.getUsername()), User::getUsername, queryDTO.getUsername())
.eq(queryDTO.getStatus() != null, User::getStatus, queryDTO.getStatus())
.eq(queryDTO.getDeptId() != null, User::getDeptId, queryDTO.getDeptId())
.between(queryDTO.getStartTime() != null && queryDTO.getEndTime() != null,
User::getCreateTime, queryDTO.getStartTime(), queryDTO.getEndTime())
.orderByDesc(User::getCreateTime);
IPage<User> userPage = this.page(page, wrapper);
// 转换为VO
return userPage.convert(this::convertToVO);
}
@Override
@Transactional(readOnly = true)
public User getByUsername(String username) {
return this.getOne(new LambdaQueryWrapper<User>().eq(User::getUsername, username));
}
@Override
public boolean resetPassword(Long userId, String newPassword) {
User user = new User();
user.setId(userId);
user.setPassword(BCrypt.hashpw(newPassword, BCrypt.gensalt()));
return this.updateById(user);
}
@Override
public boolean updateStatus(Long userId, Integer status) {
    return this.lambdaUpdate()
        .set(User::getStatus, status)
        .eq(User::getId, userId)
        .update();
}
private UserVO convertToVO(User user) {
    UserVO vo = new UserVO();
    BeanUtils.copyProperties(user, vo);
    // 处理特别字段转换
    return vo;
}
}

4. 控制器层设计规范
@RestController
@RequestMapping("/api/users")
@Api(tags = "用户管理")
@Validated
public class UserController {
    @Autowired
    private UserService userService;
    
    @GetMapping("/page")
    @ApiOperation("分页检索用户")
    public Result> pageUsers(@Valid UserQueryDTO queryDTO) {
        IPage page = userService.pageUsers(queryDTO);
        return Result.success(page);
    }
    
    @GetMapping("/{id}")
    @ApiOperation("依据ID检索用户")
    public Result getUser(@PathVariable Long id) {
        User user = userService.getById(id);
        if (user == null) {
            return Result.error("用户不存在");
        }
        UserVO vo = convertToVO(user);
        return Result.success(vo);
    }
    
    @PostMapping
    @ApiOperation("添加新用户")
    public Result addUser(@Valid @RequestBody UserSaveDTO saveDTO) {
        User user = convertToEntity(saveDTO);
        boolean success = userService.save(user);
        return Result.success(success);
    }
    
    @PutMapping("/{id}")
    @ApiOperation("修改用户信息")
    public Result updateUser(@PathVariable Long id, @Valid @RequestBody UserSaveDTO saveDTO) {
        User user = convertToEntity(saveDTO);
        user.setId(id);
        boolean success = userService.updateById(user);
        return Result.success(success);
    }
    
    @DeleteMapping("/{id}")
    @ApiOperation("移除用户")
    public Result deleteUser(@PathVariable Long id) {
        boolean success = userService.removeById(id);
        return Result.success(success);
    }
    
    @PutMapping("/{id}/status")
    @ApiOperation("更新用户状态")
    public Result updateStatus(@PathVariable Long id, @RequestParam Integer status) {
        boolean success = userService.updateStatus(id, status);
        return Result.success(success);
    }
}

5. 批量操作优化
@Service
public class BatchOperationService {
    @Autowired
    private UserService userService;
    
    /**
     * 批量保存用户
     */
    @Transactional(rollbackFor = Exception.class)
public boolean batchSaveUsers(List<User> users) {
    // 分段处理,防止内存溢出
    int batchSize = 1000;
    return userService.saveBatch(users, batchSize);
}

/**
 * 大规模更新用户状态
 */
@Transactional(rollbackFor = Exception.class)
public boolean batchUpdateStatus(List<Long> userIds, Integer status) {
    if (CollectionUtils.isEmpty(userIds)) {
        return false;
    }
    return userService.lambdaUpdate()
            .set(User::getStatus, status)
            .in(User::getId, userIds)
            .update();
}

/**
 * 大规模移除用户
 */
@Transactional(rollbackFor = Exception.class)
public boolean batchDeleteUsers(List<Long> userIds) {
    if (CollectionUtils.isEmpty(userIds)) {
        return false;
    }
    // 逻辑移除
    return userService.removeByIds(userIds);
}

常见问题与解决方案

  1. 字段映射问题

    问题:数据库字段名称与实体属性名称不符

    • 解决方案1:使用@TableField注解
    • @TableField("user_name")
      private String userName;
    • 解决方案2:全局设置驼峰转换
    • mybatis-plus:
      configuration:
      map-underscore-to-camel-case: true
    • 解决方案3:自定义字段策略
    • @TableName(value = "user", resultMap = "userResultMap")
      public class User {
          // ...
      }

    问题:字段不参与序列化

    • 使用@JsonIgnore:
    • @JsonIgnore
      @TableField("password")
      private String password;
    • 使用@TableField(select = false):
    • @TableField(value = "password", select = false)
      private String password;
  2. 主键生成问题

    问题:自定义主键生成策略

    @Component
    public class CustomIdGenerator implements IdentifierGenerator {
        @Override
        public Long nextId(Object entity) {
            // 自定义ID生成逻辑
            return System.currentTimeMillis() + RandomUtils.nextLong(1000, 9999);
        }
    
        @Override
        public String nextUUID(Object entity) {
            return UUID.randomUUID().toString().replace("-", "");
        }
    }

    使用自定义生成器:

    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
  3. 分页查询问题

    问题:分页查询总数不精确

    @Configuration
    public class MybatisPlusConfig {
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
            // 优化count查询
            paginationInterceptor.setOptimizeJoin(true);
            interceptor.addInnerInterceptor(paginationInterceptor);
            return interceptor;
        }
    }

    问题:大量数据分页性能问题

    解决方案:采用游标分页

public List<User> getUsersByCursor(Long lastId, Integer size) {
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    wrapper.gt(lastId != null, User::getId, lastId)
            .orderByAsc(User::getId)
            .last("LIMIT " + size);
    return userMapper.selectList(wrapper);
}

4. 乐观锁问题
问题:乐观锁更新不成功
// 设置乐观锁插件
@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 添加乐观锁插件
        OptimisticLockerInnerInterceptor optimisticLockerInterceptor = new OptimisticLockerInnerInterceptor();
        interceptor.addInnerInterceptor(optimisticLockerInterceptor);
        return interceptor;
    }
}

// 实体类中加入版本字段
@Version
@TableField("version")
private Integer version;

// 解决乐观锁更新不成功的问题
@Service
public class UserService {
    public boolean updateUserWithRetry(User user) {
        int maxRetries = 3;
        int retryCount = 0;
        while (retryCount < maxRetries) {
            try {
                // 查询最新的版本
                User latestUser = this.getById(user.getId());
                user.setVersion(latestUser.getVersion());
                boolean success = this.updateById(user);
                if (success) {
                    return true;
                }
            } catch (OptimisticLockingFailureException e) {
                retryCount++;
                if (retryCount >= maxRetries) {
                    throw new BusinessException("更新失败,请稍后再试");
                }
            }
        }
        return false;
    }
}

5. 逻辑删除问题
问题:逻辑删除后的数据仍被查询到
// 全局设置逻辑删除
mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted
      logic-delete-value: 1
      logic-not-delete-value: 0

// 实体类设置
@TableLogic
@TableField("deleted")
private Integer deleted;

// 查询时包含已删除的数据
List<User> allUsers = userMapper.selectList(
    new QueryWrapper<User>().eq("deleted", 1)
);

6. SQL注入防御
问题:动态SQL存在注入威胁
// 错误的方法
public List<User> getUsersByName(String name) {
    return userMapper.selectList(
        new QueryWrapper<User>().apply("name = '" + name + "'")
    );
}

// 正确的方法
public List<User> getUsersByName(String name) {
    return userMapper.selectList(
        new LambdaQueryWrapper<User>().eq(User::getName, name)
    );
}

// 使用参数化查询
public List<User> getUsersByCondition(String condition) {
    return userMapper.selectList(
new QueryWrapper<User>().apply("name = {0}", condition)
);
}
7. 效率提升
问题:N+1查询问题
// 不正确的写法:会引发N+1查询
public List<UserVO> getUsersWithDept() {
List<User> users = userService.list();
return users.stream().map(user -> {
UserVO vo = new UserVO();
BeanUtils.copyProperties(user, vo);
// 这部分会触发多次查询
Dept dept = deptService.getById(user.getDeptId());
vo.setDeptName(dept.getName());
return vo;
}).collect(Collectors.toList());
}
// 正确的写法:采用联表查询
@Mapper
public interface UserMapper extends BaseMapper<User> {
@Select("SELECT u.*, d.name as dept_name FROM user u LEFT JOIN dept d ON u.dept_id = d.id")
List<UserVO> selectUsersWithDept();
}
问题:大规模操作效率问题
// 优化批量插入
@Service
public class BatchService {
@Autowired
private SqlSessionFactory sqlSessionFactory;
@Transactional(rollbackFor = Exception.class)
public void batchInsertOptimized(List<User> users) {
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
for (int i = 0; i < users.size(); i++) {
mapper.insert(users.get(i));
// 每1000条记录提交一次
if (i % 1000 == 0 || i == users.size() - 1) {
sqlSession.flushStatements();
}
}
sqlSession.commit();
} finally {
sqlSession.close();
}
}
}
8. 多数据源设置
问题:配置多个数据源
@Configuration
public class DataSourceConfig {
@Bean
@Primary
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean
@Primary
public SqlSessionFactory primarySqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource) throws Exception {
MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath*:mapper/primary/**/*.xml"));
return sessionFactory.getObject();
}
    
@Bean
public SqlSessionFactory secondarySqlSessionFactory(@Qualifier("secondaryDataSource") DataSource dataSource) throws Exception {
    MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
    sessionFactory.setDataSource(dataSource);
    sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
        .getResources("classpath*:mapper/secondary/**/*.xml"));
    return sessionFactory.getObject();
}
}
// 利用@DS注解转换数据源
@Service
public class UserService {
    @DS("primary")
    public List<User> getPrimaryUsers() {
        return userMapper.selectList(null);
    }
    @DS("secondary")
    public List<User> getSecondaryUsers() {
        return userMapper.selectList(null);
    }
}
9. 事务管理
问题:声明式事务失灵
// 错误示例:方法必需公开
@Transactional
private void updateUser(User user) {
    userService.updateById(user);
}
// 正确示例
@Transactional(rollbackFor = Exception.class)
public void updateUser(User user) {
    userService.updateById(user);
}
// 程序化事务
@Service
public class UserService {
    @Autowired
    private TransactionTemplate transactionTemplate;
    public boolean updateUserWithTransaction(User user) {
        return transactionTemplate.execute(status -> {
            try {
                boolean success = this.updateById(user);
                if (!success) {
                    status.setRollbackOnly();
                    return false;
                }
                // 其他业务逻辑
                return true;
            } catch (Exception e) {
                status.setRollbackOnly();
                throw new RuntimeException(e);
            }
        });
    }
}
10. 配置改进
完整的生产环境配置示例
# application-prod.yml
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_plus?useSSL=true&serverTimezone=UTC&characterEncoding=UTF-8&allowPublicKeyRetrieval=true
    username: ${DB_USERNAME:root}
    password: ${DB_PASSWORD:123456}
    type: com.alibaba.druid.pool.DruidDataSource
  druid:
    initial-size: 10
    min-idle: 10
    max-active: 50
    max-wait: 60000
    time-between-eviction-runs-millis: 60000
    min-evictable-idle-time-millis: 300000
    test-while-idle: true
    test-on-borrow: false
    test-on-return: false
    validation-query: SELECT 1
    pool-prepared-statements: true
    max-pool-prepared-statement-per-connection-size: 20
    filters: stat,wall,slf4j

connection-properties:druid.stat.mergeSql=true;druid.stat.slowSqlMillis=2000

web-stat-filter:

enabled:true

url-pattern:/*

exclusions:"*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"

stat-view-servlet:

enabled:true

url-pattern:/druid/*

login-username:admin

login-password:123456

mybatis-plus:

configuration:

log-impl:org.apache.ibatis.logging.slf4j.Slf4jImpl

map-underscore-to-camel-case:true

cache-enabled:true

local-cache-scope:session

jdbc-type-for-null:null

call-setters-on-nulls:true

return-instance-for-empty-row:true

global-config:

banner:false

db-config:

id-type:auto

table-prefix:t_

logic-delete-field:deleted

logic-delete-value:1

logic-not-delete-value:0

insert-strategy:not_null

update-strategy:not_null

select-strategy:not_null

mapper-locations:classpath*:mapper/**/*.xml

type-aliases-package:com.example.entity

type-handlers-package:com.example.handler

check-config-location:true

executor-type:simple

logging:

level:

com.example.mapper:debug

com.alibaba.druid:info

org.springframework.transaction:debug

pattern:

console:"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"

file:"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"

file:

name:logs/mybatis-plus-app.log

max-size:100MB

max-history:30

总结

MyBatis-Plus作为MyBatis的强化工具,显著提升了开发效率,减少了模板代码。通过本文的学习,你应已掌握:

  • 基础使用:环境构建、基本设置、CRUD操作
  • 核心功能:注释应用、条件构建器、分页插件
  • 高级特性:代码生成器、多租户支持、数据权限、性能分析
  • 最佳实践:项目结构规划、性能优化、异常处理
  • 问题解决:常见问题的分析与对策

在实际项目中,建议:

  • 适当使用注释,确保代码整洁
  • 充分利用条件构建器,减少手动编写SQL
  • 配置适宜的插件,提升开发效率
  • 注重性能优化,尤其是大数据量场景
  • 妥善处理异常和事务管理
  • 遵循最佳实践,保障代码质量
二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

栏目导航
热门文章
推荐文章

说点什么

分享

扫码加好友,拉您进群
各岗位、行业、专业交流群