百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

SpringBoot整合MyBatis-Plus:从入门到精通

mhr18 2025-06-03 23:59 8 浏览 0 评论

一、MyBatis-Plus基础介绍

1.1 MyBatis-Plus核心概念

MyBatis-Plus(简称MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生。它具有以下特性:

特性

说明

优势

无侵入

只做增强不做改变,引入它不会对现有工程产生影响

可以平滑地切换到MyBatis-Plus

损耗小

启动即会自动注入基本CRUD操作

基本CRUD操作无需再手动编写

强大的CRUD操作

内置通用Mapper、通用Service

少量配置即可实现大部分CRUD操作

支持Lambda形式调用

通过Lambda表达式,方便的编写各类查询条件

代码更简洁,更易维护

支持主键自动生成

支持多达4种主键策略(内含分布式唯一ID生成器)

解决分布式系统ID生成问题

支持ActiveRecord模式

支持ActiveRecord形式调用,实体类只需继承Model类即可进行CRUD操作

更简单的CRUD实现方式

内置代码生成器

采用代码或Maven插件可快速生成Mapper、Model、Service、Controller层代码

减少约80%的编码工作

内置分页插件

基于MyBatis物理分页,开发者无需关心具体操作

配置好插件后,写分页等同于普通List查询

内置性能分析插件

可输出SQL语句以及其执行时间

能快速定位慢查询

内置全局拦截插件

提供全表delete、update操作智能分析阻断

预防误操作

内置SQL注入剥离器

支持SQL注入剥离,有效预防SQL注入攻击

系统更安全

1.2 MyBatis-Plus与MyBatis的关系

MyBatis-Plus并不是要取代MyBatis,而是在MyBatis的基础上提供更多便利的功能。它们之间的关系可以用以下图示表示:

MyBatis-Plus保留了MyBatis的所有特性,同时添加了以下增强功能:

  1. 自动生成基本CRUD操作
  2. 条件构造器简化复杂查询
  3. 多种主键生成策略
  4. ActiveRecord模式支持
  5. 内置分页、性能分析等插件

1.3 MyBatis-Plus适用场景

MyBatis-Plus特别适合以下开发场景:

  1. 需要快速开发的Web应用
  2. 需要大量标准CRUD操作的系统
  3. 需要简化MyBatis配置的项目
  4. 需要统一代码风格和规范的团队
  5. 需要快速生成基础代码的原型项目

二、SpringBoot整合MyBatis-Plus基础配置

2.1 环境准备与依赖引入

在开始整合之前,我们需要准备以下环境:

  1. JDK 1.8+
  2. Maven 3.0+
  3. SpringBoot 2.x
  4. 数据库(MySQL 5.7+)

在pom.xml中添加必要的依赖:

<!-- SpringBoot Starter -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- MyBatis-Plus Starter -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.1</version>
</dependency>

<!-- 数据库驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

<!-- Lombok简化代码 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

2.2 数据库配置

在application.yml中配置数据库连接信息:

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

# MyBatis-Plus配置
mybatis-plus:
  configuration:
    # 日志实现
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 下划线转驼峰
    map-underscore-to-camel-case: true
  global-config:
    db-config:
      # 主键类型 AUTO:"数据库ID自增", INPUT:"用户输入ID", ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
      id-type: auto
      # 逻辑删除配置
      logic-delete-field: deleted  # 全局逻辑删除的实体字段名
      logic-delete-value: 1        # 逻辑已删除值(默认为1)
      logic-not-delete-value: 0    # 逻辑未删除值(默认为0)

2.3 实体类与Mapper创建

2.3.1 创建基础实体类

import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.util.Date;

@Data
@TableName("user")  // 指定表名,如果表名与类名一致(忽略大小写),可以省略
public class User {
    /**
     * 主键
     * @TableId 注解用于标识主键
     * type = IdType.AUTO 表示数据库ID自增
     */
    @TableId(type = IdType.AUTO)
    private Long id;
    
    /**
     * 用户名
     */
    private String username;
    
    /**
     * 密码
     */
    private String password;
    
    /**
     * 年龄
     */
    private Integer age;
    
    /**
     * 邮箱
     */
    private String email;
    
    /**
     * 创建时间
     * @TableField 注解用于标识非主键字段
     * fill = FieldFill.INSERT 表示插入时自动填充
     */
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    
    /**
     * 更新时间
     * fill = FieldFill.INSERT_UPDATE 表示插入和更新时自动填充
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    
    /**
     * 逻辑删除字段
     * value 未删除的值
     * delval 已删除的值
     */
    @TableLogic
    private Integer deleted;
}

2.3.2 创建Mapper接口

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.entity.User;

/**
 * UserMapper接口
 * 继承BaseMapper<T>即可获得基本的CRUD功能
 * 无需编写mapper.xml文件
 */
public interface UserMapper extends BaseMapper<User> {
    // 可以在这里添加自定义的接口方法
    // 如果需要自定义SQL,可以在resources/mapper/UserMapper.xml中编写
}

2.3.3 添加Mapper扫描

在SpringBoot启动类上添加@MapperScan注解:

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

@SpringBootApplication
@MapperScan("com.example.demo.mapper")  // 扫描Mapper接口所在的包
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

2.4 自动填充功能实现

MyBatis-Plus提供了自动填充功能,可以在插入或更新时自动填充某些字段(如创建时间、更新时间等)。实现步骤如下:

  1. 创建元对象处理器:
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;

/**
 * 自动填充处理器
 */
@Component  // 不要忘记加@Component注解
public class MyMetaObjectHandler implements MetaObjectHandler {
    
    /**
     * 插入时的填充策略
     * @param metaObject 元对象
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        // 根据属性名称设置填充值
        this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
        this.strictInsertFill(metaObject, "updateTime", Date.class, new Date());
    }
    
    /**
     * 更新时的填充策略
     * @param metaObject 元对象
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        // 根据属性名称设置填充值
        this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
    }
}

三、MyBatis-Plus基础CRUD操作

3.1 插入操作

MyBatis-Plus提供了多种插入方法:

import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.ArrayList;
import java.util.List;

@SpringBootTest
public class InsertTest {
    
    @Autowired
    private UserMapper userMapper;
    
    /**
     * 插入一条记录
     */
    @Test
    public void testInsert() {
        User user = new User();
        user.setUsername("张三");
        user.setPassword("123456");
        user.setAge(20);
        user.setEmail("zhangsan@example.com");
        
        // insert方法会返回影响的行数
        int rows = userMapper.insert(user);
        System.out.println("影响行数:" + rows);
        
        // 插入后,自动生成的主键会回填到实体对象中
        System.out.println("自动生成的主键ID:" + user.getId());
    }
    
    /**
     * 批量插入
     */
    @Test
    public void testBatchInsert() {
        List<User> userList = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            User user = new User();
            user.setUsername("用户" + i);
            user.setPassword("pwd" + i);
            user.setAge(20 + i);
            user.setEmail("user" + i + "@example.com");
            userList.add(user);
        }
        
        // 循环插入
        for (User user : userList) {
            userMapper.insert(user);
        }
        
        // 或者使用Service层的saveBatch方法(后面会介绍)
    }
}

3.2 更新操作

MyBatis-Plus提供了多种更新方法:

import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;

@SpringBootTest
public class UpdateTest {
    
    @Autowired
    private UserMapper userMapper;
    
    /**
     * 根据ID更新
     */
    @Test
    public void testUpdateById() {
        // 先查询出要更新的实体
        User user = userMapper.selectById(1L);
        
        // 修改字段值
        user.setUsername("张三修改后");
        user.setAge(25);
        
        // 根据ID更新,null值字段不会更新
        int rows = userMapper.updateById(user);
        System.out.println("影响行数:" + rows);
    }
    
    /**
     * 条件更新
     */
    @Test
    public void testUpdate() {
        // 方法一:使用UpdateWrapper
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("username", "张三")
                    .set("age", 30)
                    .set("email", "newemail@example.com");
        
        int rows = userMapper.update(null, updateWrapper);
        System.out.println("影响行数:" + rows);
        
        // 方法二:使用LambdaUpdateWrapper(推荐)
        User user = new User();
        user.setAge(35);
        
        int rows2 = userMapper.update(user, 
            new UpdateWrapper<User>().lambda()
                .eq(User::getUsername, "张三")
        );
        System.out.println("影响行数:" + rows2);
    }
}

3.3 删除操作

MyBatis-Plus提供了多种删除方法:

import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import java.util.Arrays;

@SpringBootTest
public class DeleteTest {
    
    @Autowired
    private UserMapper userMapper;
    
    /**
     * 根据ID删除
     */
    @Test
    public void testDeleteById() {
        int rows = userMapper.deleteById(1L);
        System.out.println("影响行数:" + rows);
    }
    
    /**
     * 根据ID批量删除
     */
    @Test
    public void testDeleteBatchIds() {
        // 参数是ID的集合
        int rows = userMapper.deleteBatchIds(Arrays.asList(2L, 3L, 4L));
        System.out.println("影响行数:" + rows);
    }
    
    /**
     * 条件删除
     */
    @Test
    public void testDelete() {
        // 构造删除条件
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("age", 20);
        
        int rows = userMapper.delete(queryWrapper);
        System.out.println("影响行数:" + rows);
    }
    
    /**
     * 逻辑删除
     * 配置了逻辑删除后,调用delete方法实际上是更新逻辑删除字段
     */
    @Test
    public void testLogicDelete() {
        int rows = userMapper.deleteById(5L);
        System.out.println("影响行数:" + rows);
        // 实际执行的是:UPDATE user SET deleted=1 WHERE id=5 AND deleted=0
    }
}

3.4 查询操作

MyBatis-Plus提供了丰富的查询方法:

import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

@SpringBootTest
public class SelectTest {
    
    @Autowired
    private UserMapper userMapper;
    
    /**
     * 根据ID查询
     */
    @Test
    public void testSelectById() {
        User user = userMapper.selectById(1L);
        System.out.println(user);
    }
    
    /**
     * 根据ID批量查询
     */
    @Test
    public void testSelectBatchIds() {
        List<User> users = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));
        users.forEach(System.out::println);
    }
    
    /**
     * 查询所有
     */
    @Test
    public void testSelectList() {
        List<User> users = userMapper.selectList(null);  // 参数为null表示查询所有
        users.forEach(System.out::println);
    }
    
    /**
     * 条件查询
     */
    @Test
    public void testSelectByWrapper() {
        // 方法一:使用QueryWrapper
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.gt("age", 20)  // age > 20
                   .like("username", "张")  // username like '%张%'
                   .isNotNull("email");  // email is not null
        
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
        
        // 方法二:使用LambdaQueryWrapper(推荐)
        List<User> users2 = userMapper.selectList(
            Wrappers.<User>lambdaQuery()
                .gt(User::getAge, 20)
                .like(User::getUsername, "张")
                .isNotNull(User::getEmail)
        );
        users2.forEach(System.out::println);
    }
    
    /**
     * 只查询部分字段
     */
    @Test
    public void testSelectPartialFields() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("id", "username", "age")  // 只查询这三个字段
                   .gt("age", 20);
        
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
        
        // 使用Lambda方式
        List<User> users2 = userMapper.selectList(
            Wrappers.<User>lambdaQuery()
                .select(User::getId, User::getUsername, User::getAge)
                .gt(User::getAge, 20)
        );
        users2.forEach(System.out::println);
    }
    
    /**
     * 查询一条记录
     */
    @Test
    public void testSelectOne() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", "张三");
        
        // 查询一条记录,如果结果多于一条会抛出异常
        User user = userMapper.selectOne(queryWrapper);
        System.out.println(user);
    }
    
    /**
     * 计数查询
     */
    @Test
    public void testSelectCount() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.gt("age", 20);
        
        Long count = userMapper.selectCount(queryWrapper);
        System.out.println("年龄大于20的用户数:" + count);
    }
    
    /**
     * 查询结果映射为Map
     */
    @Test
    public void testSelectMaps() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("username", "age")
                   .gt("age", 20);
        
        List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
        maps.forEach(System.out::println);
    }
}

四、条件构造器详解

4.1 条件构造器概述

MyBatis-Plus提供了强大的条件构造器,可以方便地构造各种查询条件。主要分为以下几类:

构造器类型

说明

QueryWrapper

查询条件构造器,用于构建WHERE条件

UpdateWrapper

更新条件构造器,用于构建UPDATE语句的SET和WHERE条件

LambdaQueryWrapper

基于Lambda表达式的查询条件构造器,避免硬编码字段名

LambdaUpdateWrapper

基于Lambda表达式的更新条件构造器,避免硬编码字段名

4.2 QueryWrapper常用方法

以下是QueryWrapper的常用方法及其说明:

4.3 Lambda条件构造器

Lambda条件构造器可以避免硬编码字段名,推荐使用:

@Test
public void testLambdaWrapper() {
    // LambdaQueryWrapper示例
    List<User> users = userMapper.selectList(
        Wrappers.<User>lambdaQuery()
            .eq(User::getUsername, "张三")
            .gt(User::getAge, 20)
            .orderByDesc(User::getCreateTime)
    );
    
    // LambdaUpdateWrapper示例
    userMapper.update(
        null,
        Wrappers.<User>lambdaUpdate()
            .set(User::getAge, 25)
            .set(User::getEmail, "newemail@example.com")
            .eq(User::getUsername, "张三")
    );
}

4.4 复杂条件组合

条件构造器支持复杂的条件组合:

@Test
public void testComplexCondition() {
    // 复杂条件示例
    List<User> users = userMapper.selectList(
        new QueryWrapper<User>()
            .select("id", "username", "age", "email")
            .gt("age", 18)
            .and(wrapper -> wrapper
                .like("username", "张")
                .or()
                .like("username", "李")
            )
            .between("create_time", "2023-01-01", "2023-12-31")
            .orderByDesc("age")
            .orderByAsc("id")
            .last("limit 10")  // 直接拼接SQL语句
    );
    
    // Lambda方式
    List<User> users2 = userMapper.selectList(
        Wrappers.<User>lambdaQuery()
            .select(User::getId, User::getUsername, User::getAge, User::getEmail)
            .gt(User::getAge, 18)
            .and(wrapper -> wrapper
                .like(User::getUsername, "张")
                .or()
                .like(User::getUsername, "李")
            )
            .between(User::getCreateTime, LocalDate.of(2023, 1, 1), LocalDate.of(2023, 12, 31))
            .orderByDesc(User::getAge)
            .orderByAsc(User::getId)
            .last("limit 10")
    );
}

五、MyBatis-Plus高级功能

5.1 分页查询

MyBatis-Plus提供了强大的分页功能,配置和使用步骤如下:

  1. 配置分页插件:
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;
    }
}
  1. 使用分页查询:
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class PageTest {
    
    @Autowired
    private UserMapper userMapper;
    
    /**
     * 基本分页查询
     */
    @Test
    public void testSelectPage() {
        // 参数1:当前页
        // 参数2:每页显示条数
        Page<User> page = new Page<>(1, 5);
        
        // 条件构造器
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.gt("age", 20);
        
        // 执行分页查询
        IPage<User> userPage = userMapper.selectPage(page, queryWrapper);
        
        // 获取分页数据
        System.out.println("总记录数:" + userPage.getTotal());
        System.out.println("总页数:" + userPage.getPages());
        System.out.println("当前页:" + userPage.getCurrent());
        System.out.println("每页大小:" + userPage.getSize());
        
        // 获取分页数据列表
        List<User> users = userPage.getRecords();
        users.forEach(System.out::println);
    }
    
    /**
     * 自定义SQL分页查询
     * 1. 在Mapper接口中定义方法
     * 2. 在Mapper.xml中编写SQL
     */
    @Test
    public void testCustomPage() {
        Page<User> page = new Page<>(1, 5);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.gt("age", 20);
        
        // 执行自定义分页查询
        IPage<User> userPage = userMapper.selectUserPage(page, queryWrapper);
        
        // 获取分页数据
        System.out.println("总记录数:" + userPage.getTotal());
        System.out.println("当前页数据:");
        userPage.getRecords().forEach(System.out::println);
    }
}

在Mapper接口中添加方法:

public interface UserMapper extends BaseMapper<User> {
    /**
     * 自定义分页查询
     * @param page 分页对象
     * @param queryWrapper 查询条件
     * @return 分页结果
     */
    IPage<User> selectUserPage(IPage<User> page, @Param(Constants.WRAPPER) Wrapper<User> queryWrapper);
}

在Mapper.xml中添加SQL:

<select id="selectUserPage" resultType="com.example.demo.entity.User">
    SELECT * FROM user ${ew.customSqlSegment}
</select>

5.2 逻辑删除

逻辑删除配置已在2.2节中展示,这里演示如何使用:

@Test
public void testLogicDelete() {
    // 删除操作实际上是更新 deleted 字段
    int rows = userMapper.deleteById(1L);
    System.out.println("影响行数:" + rows);
    
    // 查询时会自动带上 deleted=0 条件
    List<User> users = userMapper.selectList(null);
    users.forEach(System.out::println);
    
    // 如果需要查询包含已删除的数据,可以使用@SqlParser注解或全局配置
}

5.3 自动填充

自动填充功能已在2.4节中配置,这里演示效果:

@Test
public void testAutoFill() {
    User user = new User();
    user.setUsername("测试自动填充");
    user.setPassword("123456");
    user.setAge(20);
    
    // 插入时自动填充createTime和updateTime
    userMapper.insert(user);
    
    // 更新时自动填充updateTime
    user.setAge(25);
    userMapper.updateById(user);
}

5.4 乐观锁

乐观锁实现步骤:

  1. 在表中添加version字段
  2. 在实体类中添加version字段并添加@Version注解
  3. 配置乐观锁插件
// 1. 实体类中添加
@Version
private Integer version;

// 2. 配置乐观锁插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    // 乐观锁插件
    interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
    return interceptor;
}

使用示例:

@Test
public void testOptimisticLocker() {
    // 查询
    User user = userMapper.selectById(1L);
    
    // 修改
    user.setUsername("new name");
    
    // 模拟其他线程已经修改了数据
    User user2 = userMapper.selectById(1L);
    user2.setUsername("other thread");
    userMapper.updateById(user2);
    
    // 当前线程更新,此时version已经不匹配,更新会失败
    int rows = userMapper.updateById(user);
    System.out.println("影响行数:" + rows);  // 0
}

六、MyBatis-Plus进阶功能

6.1 ActiveRecord模式

ActiveRecord模式是一种数据访问设计模式,它允许实体类直接操作数据库:

  1. 实体类继承Model类:
import com.baomidou.mybatisplus.extension.activerecord.Model;

@Data
@TableName("user")
public class User extends Model<User> {
    // 字段定义...
}
  1. 使用示例:
@Test
public void testActiveRecord() {
    // 插入
    User user = new User();
    user.setUsername("AR模式");
    user.setPassword("123456");
    user.setAge(20);
    user.insert();  // 直接调用实体类的方法
    
    // 查询
    User user2 = new User();
    user2.setId(1L);
    User result = user2.selectById();
    System.out.println(result);
    
    // 更新
    user2.setUsername("修改后的名字");
    user2.updateById();
    
    // 删除
    user2.deleteById();
    
    // 条件查询
    List<User> users = user2.selectList(
        new QueryWrapper<User>().gt("age", 20)
    );
}

6.2 多数据源配置

在实际项目中,有时需要连接多个数据库:

  1. 添加依赖:
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.5.1</version>
</dependency>
  1. 配置多数据源:
spring:
  datasource:
    dynamic:
      primary: master # 设置默认的数据源或者数据源组,默认值即为master
      strict: false # 严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/master?useSSL=false
          username: root
          password: 123456
          driver-class-name: com.mysql.cj.jdbc.Driver
        slave1:
          url: jdbc:mysql://localhost:3306/slave1?useSSL=false
          username: root
          password: 123456
          driver-class-name: com.mysql.cj.jdbc.Driver
        slave2:
          url: jdbc:mysql://localhost:3306/slave2?useSSL=false
          username: root
          password: 123456
          driver-class-name: com.mysql.cj.jdbc.Driver
  1. 使用@DS注解切换数据源:
@Service
@DS("master") // 默认使用master数据源
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    
    @DS("slave1") // 方法上使用slave1数据源
    public List<User> getAllFromSlave1() {
        return baseMapper.selectList(null);
    }
    
    @DS("slave2") // 方法上使用slave2数据源
    public List<User> getAllFromSlave2() {
        return baseMapper.selectList(null);
    }
}

6.3 代码生成器

MyBatis-Plus提供了强大的代码生成器:

  1. 添加依赖:
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.5.1</version>
</dependency>
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.31</version>
</dependency>
  1. 创建生成器代码:
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 CodeGenerator {
    public static void main(String[] args) {
        FastAutoGenerator.create("jdbc:mysql://localhost:3306/mp_demo?useSSL=false", "root", "123456")
            .globalConfig(builder -> {
                builder.author("author") // 设置作者
                    .enableSwagger() // 开启swagger模式
                    .fileOverride() // 覆盖已生成文件
                    .outputDir("D://mybatis-plus"); // 指定输出目录
            })
            .packageConfig(builder -> {
                builder.parent("com.example") // 设置父包名
                    .moduleName("demo") // 设置父包模块名
                    .pathInfo(Collections.singletonMap(OutputFile.xml, "D://mybatis-plus")); // 设置mapperXml生成路径
            })
            .strategyConfig(builder -> {
                builder.addInclude("user") // 设置需要生成的表名
                    .addTablePrefix("t_", "c_"); // 设置过滤表前缀
            })
            .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
            .execute();
    }
}

6.4 自定义SQL注入器

MyBatis-Plus允许自定义SQL方法:

  1. 定义自定义方法:
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;

public class DeleteAllMethod extends AbstractMethod {
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        // 执行的SQL
        String sql = "delete from " + tableInfo.getTableName();
        // Mapper接口方法名
        String method = "deleteAll";
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        return this.addDeleteMappedStatement(mapperClass, method, sqlSource);
    }
}
  1. 创建SQL注入器:
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import java.util.List;

public class MySqlInjector extends DefaultSqlInjector {
    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
        List<AbstractMethod> methodList = super.getMethodList(mapperClass);
        // 添加自定义方法
        methodList.add(new DeleteAllMethod());
        return methodList;
    }
}
  1. 注册SQL注入器:
@Bean
public MySqlInjector mySqlInjector() {
    return new MySqlInjector();
}
  1. 在Mapper接口中添加方法:
public interface UserMapper extends BaseMapper<User> {
    /**
     * 自定义方法:删除所有数据
     * @return 影响行数
     */
    int deleteAll();
}
  1. 使用自定义方法:
@Test
public void testCustomMethod() {
    int rows = userMapper.deleteAll();
    System.out.println("删除行数:" + rows);
}

七、MyBatis-Plus最佳实践

7.1 性能优化建议

  1. 合理使用索引:为查询条件中的字段添加索引
  2. 避免全表扫描:尽量不要使用不带条件的selectList(null)
  3. 只查询需要的字段:使用select()方法指定查询字段
  4. 合理使用缓存:对于不常变的数据可以使用缓存
  5. 批量操作:使用批量插入、更新方法减少数据库交互次数

7.2 事务管理

SpringBoot中结合@Transactional使用MyBatis-Plus:

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserService {
    
    @Autowired
    private UserMapper userMapper;
    
    /**
     * 事务示例
     */
    @Transactional(rollbackFor = Exception.class)
    public void transferMoney(Long fromUserId, Long toUserId, int amount) {
        // 扣钱
        User fromUser = userMapper.selectById(fromUserId);
        fromUser.setMoney(fromUser.getMoney() - amount);
        userMapper.updateById(fromUser);
        
        // 加钱
        User toUser = userMapper.selectById(toUserId);
        toUser.setMoney(toUser.getMoney() + amount);
        userMapper.updateById(toUser);
        
        // 如果这里抛出异常,事务会回滚
        // int i = 1 / 0;
    }
}

7.3 异常处理

MyBatis-Plus常见的异常及处理方式:

异常类型

原因

解决方案

MybatisPlusException

MyBatis-Plus通用异常

检查错误信息,修正相应操作

TooManyResultsException

selectOne方法返回了多条结果

确保查询条件能唯一确定一条记录,或使用selectList

PagingException

分页参数错误

检查分页参数是否合法

TableInfoException

实体类与数据库表映射错误

检查@TableName注解和表名是否正确

SqlInjectorException

SQL注入器异常

检查自定义SQL注入器实现

MyBatisSystemException

MyBatis系统异常

检查SQL语句和映射配置

全局异常处理示例:

import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler {
    
    /**
     * 处理MyBatis-Plus异常
     */
    @ExceptionHandler(MybatisPlusException.class)
    public Result handleMybatisPlusException(MybatisPlusException e) {
        return Result.fail(e.getMessage());
    }
    
    /**
     * 处理其他异常
     */
    @ExceptionHandler(Exception.class)
    public Result handleException(Exception e) {
        return Result.fail("系统异常:" + e.getMessage());
    }
}

7.4 与SpringBoot其他组件整合

  1. 整合Swagger生成API文档:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
            .apiInfo(new ApiInfoBuilder()
                .title("MyBatis-Plus Demo API")
                .description("MyBatis-Plus整合示例")
                .version("1.0")
                .build())
            .select()
            .apis(RequestHandlerSelectors.basePackage("com.example.demo.controller"))
            .paths(PathSelectors.any())
            .build();
    }
}
  1. 整合Redis缓存:
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;

@Configuration
@EnableCaching
public class RedisConfig {
    
    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(30))  // 设置缓存有效期30分钟
            .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
            .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
            .disableCachingNullValues();
        
        return RedisCacheManager.builder(connectionFactory)
            .cacheDefaults(config)
            .transactionAware()
            .build();
    }
}

在Service中使用缓存:

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    
    @Cacheable(value = "user", key = "#id")
    @Override
    public User getById(Serializable id) {
        return super.getById(id);
    }
    
    @CacheEvict(value = "user", key = "#user.id")
    @Override
    public boolean updateById(User user) {
        return super.updateById(user);
    }
}

八、实际项目案例

8.1 用户管理系统实现

下面我们实现一个完整的用户管理模块,包含以下功能:

  1. 用户分页查询
  2. 用户添加/修改
  3. 用户删除(逻辑删除)
  4. 用户条件查询

8.1.1 实体类

@Data
@TableName("sys_user")
@ApiModel(value = "User对象", description = "用户表")
public class User extends Model<User> {
    
    @TableId(type = IdType.AUTO)
    @ApiModelProperty(value = "主键ID")
    private Long id;
    
    @ApiModelProperty(value = "用户名")
    private String username;
    
    @ApiModelProperty(value = "密码")
    private String password;
    
    @ApiModelProperty(value = "年龄")
    private Integer age;
    
    @ApiModelProperty(value = "邮箱")
    private String email;
    
    @ApiModelProperty(value = "角色ID")
    private Long roleId;
    
    @TableField(fill = FieldFill.INSERT)
    @ApiModelProperty(value = "创建时间")
    private Date createTime;
    
    @TableField(fill = FieldFill.INSERT_UPDATE)
    @ApiModelProperty(value = "更新时间")
    private Date updateTime;
    
    @TableLogic
    @ApiModelProperty(value = "逻辑删除标志")
    private Integer deleted;
    
    @TableField(exist = false)
    @ApiModelProperty(value = "角色名称")
    private String roleName;
}

8.1.2 Mapper接口

public interface UserMapper extends BaseMapper<User> {
    
    /**
     * 自定义分页查询(包含角色名称)
     * @param page 分页对象
     * @param queryWrapper 查询条件
     * @return 分页结果
     */
    IPage<User> selectUserPageWithRole(IPage<User> page, @Param(Constants.WRAPPER) Wrapper<User> queryWrapper);
}

8.1.3 Mapper XML

<select id="selectUserPageWithRole" resultType="com.example.demo.entity.User">
    SELECT u.*, r.name as role_name 
    FROM sys_user u
    LEFT JOIN sys_role r ON u.role_id = r.id
    ${ew.customSqlSegment}
</select>

8.1.4 Service接口

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;

public interface UserService extends IService<User> {
    
    /**
     * 分页查询用户列表(包含角色名称)
     * @param page 分页参数
     * @param user 查询条件
     * @return 分页结果
     */
    IPage<User> selectUserPageWithRole(IPage<User> page, User user);
}

8.1.5 Service实现

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    
    @Override
    public IPage<User> selectUserPageWithRole(IPage<User> page, User user) {
        return baseMapper.selectUserPageWithRole(page, 
            Wrappers.<User>lambdaQuery()
                .like(StringUtils.isNotBlank(user.getUsername()), User::getUsername, user.getUsername())
                .eq(user.getAge() != null, User::getAge, user.getAge())
                .eq(user.getRoleId() != null, User::getRoleId, user.getRoleId())
        );
    }
}

8.1.6 Controller

@RestController
@RequestMapping("/user")
@Api(tags = "用户管理")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    @GetMapping("/page")
    @ApiOperation("分页查询用户列表")
    public Result<IPage<User>> page(
            @RequestParam(defaultValue = "1") Integer pageNum,
            @RequestParam(defaultValue = "10") Integer pageSize,
            User user) {
        Page<User> page = new Page<>(pageNum, pageSize);
        return Result.success(userService.selectUserPageWithRole(page, user));
    }
    
    @PostMapping
    @ApiOperation("添加用户")
    public Result<Void> add(@RequestBody User user) {
        userService.save(user);
        return Result.success();
    }
    
    @PutMapping
    @ApiOperation("修改用户")
    public Result<Void> update(@RequestBody User user) {
        userService.updateById(user);
        return Result.success();
    }
    
    @DeleteMapping("/{id}")
    @ApiOperation("删除用户")
    public Result<Void> delete(@PathVariable Long id) {
        userService.removeById(id);
        return Result.success();
    }
    
    @GetMapping("/{id}")
    @ApiOperation("根据ID查询用户")
    public Result<User> getById(@PathVariable Long id) {
        return Result.success(userService.getById(id));
    }
}

8.1.7 统一返回结果

@Data
@ApiModel(value = "统一返回结果")
public class Result<T> implements Serializable {
    
    @ApiModelProperty(value = "状态码")
    private Integer code;
    
    @ApiModelProperty(value = "返回消息")
    private String message;
    
    @ApiModelProperty(value = "返回数据")
    private T data;
    
    public static <T> Result<T> success() {
        return success(null);
    }
    
    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>();
        result.setCode(200);
        result.setMessage("成功");
        result.setData(data);
        return result;
    }
    
    public static <T> Result<T> fail(String message) {
        return fail(500, message);
    }
    
    public static <T> Result<T> fail(Integer code, String message) {
        Result<T> result = new Result<>();
        result.setCode(code);
        result.setMessage(message);
        return result;
    }
}

8.2 复杂查询示例

8.2.1 多表关联查询

@Test
public void testMultiTableQuery() {
    // 查询用户及其角色信息
    List<User> users = userMapper.selectList(
        Wrappers.<User>query()
            .select("u.*", "r.name as role_name")
            .from("sys_user u")
            .leftJoin("sys_role r on u.role_id = r.id")
            .eq("u.deleted", 0)
            .orderByAsc("u.create_time")
    );
    
    // 使用Lambda方式
    List<User> users2 = userMapper.selectList(
        Wrappers.<User>lambdaQuery()
            .select(User.class, info -> 
                !info.getColumn().equals("password") && 
                !info.getColumn().equals("deleted"))
            .eq(User::getDeleted, 0)
            .orderByAsc(User::getCreateTime)
    );
}

8.2.2 子查询

@Test
public void testSubQuery() {
    // 查询年龄大于平均年龄的用户
    List<User> users = userMapper.selectList(
        Wrappers.<User>query()
            .inSql("id", "select id from sys_user where age > (select avg(age) from sys_user)")
    );
    
    // 使用Lambda方式
    List<User> users2 = userMapper.selectList(
        Wrappers.<User>lambdaQuery()
            .inSql(User::getId, "select id from sys_user where age > (select avg(age) from sys_user)")
    );
}

8.2.3 分组统计

@Test
public void testGroupBy() {
    // 按角色分组统计用户数量
    List<Map<String, Object>> result = userMapper.selectMaps(
        Wrappers.<User>query()
            .select("role_id", "count(*) as user_count")
            .groupBy("role_id")
    );
    
    // 使用Lambda方式
    List<Map<String, Object>> result2 = userMapper.selectMaps(
        Wrappers.<User>lambdaQuery()
            .select(User::getRoleId, "count(*) as user_count")
            .groupBy(User::getRoleId)
    );
}

九、MyBatis-Plus常见问题与解决方案

9.1 常见问题汇总

问题描述

可能原因

解决方案

字段值为null时没有更新

MyBatis-Plus默认策略是null值不更新

1. 在字段上添加@TableField(update="%s") 2. 使用UpdateWrapper的set方法

实体类属性名与数据库字段名不一致

未正确配置字段映射

1. 使用@TableField注解指定字段名 2. 配置全局下划线转驼峰

分页查询不生效

未配置分页插件

按照5.1节配置分页插件

逻辑删除不生效

1. 未配置逻辑删除 2. 字段名配置错误

1. 按照2.2节配置逻辑删除 2. 检查字段名是否正确

自动填充不生效

1. 未实现MetaObjectHandler 2. 字段注解配置错误

1. 按照2.4节实现处理器 2. 检查@TableField注解配置

多数据源切换无效

1. 未正确配置多数据源 2. @DS注解位置不正确

1. 检查多数据源配置 2. @DS注解应加在Service类或方法上

批量插入效率低

使用了循环单条插入

使用Service的saveBatch方法

生成的SQL不符合预期

条件构造器使用错误

检查条件构造器的链式调用是否正确

实体类继承Model后CRUD方法冲突

与Service层方法冲突

避免同时使用ActiveRecord模式和Service模式

9.2 性能调优建议

  1. 批量操作:使用saveBatch、updateBatchById等方法减少数据库交互次数
  2. 合理使用索引:为查询条件中的字段添加索引
  3. 只查询必要字段:避免使用select *,只查询需要的字段
  4. 避免N+1查询:使用@TableField(exist = false)标注非数据库字段,避免自动查询
  5. 合理使用缓存:对于不常变的数据使用缓存
  6. SQL优化:使用MyBatis-Plus的SQL打印功能,分析慢SQL并进行优化
  7. 分页优化:对于大数据量分页,使用优化后的分页方式:
// 优化后的分页查询方式(对于大数据量)
@Test
public void testOptimizePage() {
    // 传统分页(大数据量性能差)
    // Page<User> page = new Page<>(10000, 10);
    
    // 优化分页(先定位到起始记录)
    Page<User> page = new Page<User>().setCurrent(10000).setSize(10);
    userMapper.selectPage(page, 
        Wrappers.<User>lambdaQuery()
            .orderByAsc(User::getId)
    );
}

9.3 安全注意事项

  1. 防止SQL注入
  2. 始终使用条件构造器而不是拼接SQL
  3. 如果必须使用SQL,使用参数化查询
  4. 敏感数据保护
  5. 密码等敏感字段不要直接存储在数据库中,应该加密存储
  6. 查询时排除敏感字段:
@Test
public void testExcludeSensitiveFields() {
    List<User> users = userMapper.selectList(
        Wrappers.<User>lambdaQuery()
            .select(User.class, info -> 
                !info.getColumn().equals("password") && 
                !info.getColumn().equals("salt"))
    );
}
  1. 权限控制
  2. 在Service层进行权限校验
  3. 使用注解进行方法级权限控制
@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(Long id) {
    userMapper.deleteById(id);
}

十、MyBatis-Plus源码解析与扩展

10.1 核心架构分析

MyBatis-Plus的核心架构可以分为以下几层:

核心组件:

  1. SqlInjector:SQL注入器,负责将BaseMapper中的方法注入到MyBatis中
  2. ISqlRunner:SQL执行器,提供直接执行SQL的能力
  3. TableInfoHelper:表信息助手,负责解析实体类与数据库表的映射关系
  4. GlobalConfig:全局配置,包含各种全局策略配置
  5. IDataSource:数据源处理器,用于多数据源场景

10.2 执行流程分析

以selectById方法为例,执行流程如下:

10.3 自定义扩展点

MyBatis-Plus提供了多个扩展点,可以按需扩展:

  1. 自定义ID生成器
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import org.springframework.stereotype.Component;

@Component
public class CustomIdGenerator implements IdentifierGenerator {
    @Override
    public Number nextId(Object entity) {
        // 实现自定义ID生成策略
        return IdWorker.getId(); // 使用默认的雪花算法
    }
    
    @Override
    public String nextUUID(Object entity) {
        // 实现自定义UUID生成策略
        return IdWorker.get32UUID();
    }
}
  1. 自定义SQL注入器(已在6.4节介绍)
  2. 自定义元对象处理器(已在2.4节介绍)
  3. 自定义分页插件
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.dialects.IDialect;

public class MyPaginationInterceptor extends PaginationInnerInterceptor {
    
    public MyPaginationInterceptor(IDialect dialect) {
        super(dialect);
    }
    
    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        // 自定义分页前处理
        super.beforeQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);
    }
    
    @Override
    public void afterQuery(Object parameter, Page<?> page) {
        // 自定义分页后处理
        super.afterQuery(parameter, page);
    }
}

10.4 插件开发示例

开发一个SQL执行时间统计插件:

import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Signature;
import java.util.Properties;

@Intercepts({
    @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class SqlCostInterceptor implements InnerInterceptor {
    
    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        // 记录开始时间
        long start = System.currentTimeMillis();
        // 将开始时间存入BoundSql
        boundSql.setAdditionalParameter("_sql_start_time", start);
    }
    
    @Override
    public void afterQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql, List<Object> result) {
        // 获取开始时间
        Long start = (Long) boundSql.getAdditionalParameter("_sql_start_time");
        if (start != null) {
            // 计算耗时
            long cost = System.currentTimeMillis() - start;
            // 打印SQL执行时间
            System.out.println("SQL执行耗时:" + cost + "ms");
            // 可以在这里添加慢SQL监控逻辑
            if (cost > 1000) {
                System.out.println("慢SQL警告:" + boundSql.getSql());
            }
        }
    }
}

配置插件:

@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    // 添加SQL耗时统计插件
    interceptor.addInnerInterceptor(new SqlCostInterceptor());
    return interceptor;
}

总结

本文全面介绍了SpringBoot整合MyBatis-Plus的各个方面,从基础配置到高级功能,再到实际项目应用和源码扩展。主要内容包括:

  1. MyBatis-Plus的核心概念与优势
  2. SpringBoot与MyBatis-Plus的整合步骤
  3. 基础CRUD操作与条件构造器的使用
  4. 高级功能如分页、逻辑删除、乐观锁等
  5. ActiveRecord模式、多数据源、代码生成器等进阶功能
  6. 性能优化、事务管理、异常处理等最佳实践
  7. 完整项目案例的实现
  8. 常见问题解决方案
  9. 源码分析与自定义扩展

MyBatis-Plus作为MyBatis的增强工具,可以极大地提高开发效率,减少样板代码的编写。通过本文的学习,相信读者已经掌握了MyBatis-Plus的核心用法,能够在实际项目中灵活运用。

最后,建议在实际项目中:

  1. 根据团队规范统一使用方式
  2. 合理使用各种高级特性
  3. 注意性能优化和安全问题
  4. 根据项目需求进行适当扩展

希望本文能对你的开发工作有所帮助!

关注我?别别别,我怕你笑出腹肌找我赔钱。


头条对markdown的文章显示不太友好,想了解更多的可以关注微信公众号:“Eric的技术杂货库”,后期会有更多的干货以及资料下载。

相关推荐

一文读懂Prometheus架构监控(prometheus监控哪些指标)

介绍Prometheus是一个系统监控和警报工具包。它是用Go编写的,由Soundcloud构建,并于2016年作为继Kubernetes之后的第二个托管项目加入云原生计算基金会(C...

Spring Boot 3.x 新特性详解:从基础到高级实战

1.SpringBoot3.x简介与核心特性1.1SpringBoot3.x新特性概览SpringBoot3.x是建立在SpringFramework6.0基础上的重大版...

「技术分享」猪八戒基于Quartz分布式调度平台实践

点击原文:【技术分享】猪八戒基于Quartz分布式调度平台实践点击关注“八戒技术团队”,阅读更多技术干货1.背景介绍1.1业务场景调度任务是我们日常开发中非常经典的一个场景,我们时常会需要用到一些不...

14. 常用框架与工具(使用的框架)

本章深入解析Go生态中的核心开发框架与工具链,结合性能调优与工程化实践,提供高效开发方案。14.1Web框架(Gin,Echo)14.1.1Gin高性能实践//中间件链优化router:=...

SpringBoot整合MyBatis-Plus:从入门到精通

一、MyBatis-Plus基础介绍1.1MyBatis-Plus核心概念MyBatis-Plus(简称MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提...

Seata源码—5.全局事务的创建与返回处理

大纲1.Seata开启分布式事务的流程总结2.Seata生成全局事务ID的雪花算法源码3.生成xid以及对全局事务会话进行持久化的源码4.全局事务会话数据持久化的实现源码5.SeataServer创...

Java开发200+个学习知识路线-史上最全(框架篇)

1.Spring框架深入SpringIOC容器:BeanFactory与ApplicationContextBean生命周期:实例化、属性填充、初始化、销毁依赖注入方式:构造器注入、Setter注...

OpenResty 入门指南:从基础到动态路由实战

一、引言1.1OpenResty简介OpenResty是一款基于Nginx的高性能Web平台,通过集成Lua脚本和丰富的模块,将Nginx从静态反向代理转变为可动态编程的应用平台...

你还在为 Spring Boot3 分布式锁实现发愁?一文教你轻松搞定!

作为互联网大厂后端开发人员,在项目开发过程中,你有没有遇到过这样的问题:多个服务实例同时访问共享资源,导致数据不一致、业务逻辑混乱?没错,这就是分布式环境下常见的并发问题,而分布式锁就是解决这类问题的...

近2万字详解JAVA NIO2文件操作,过瘾

原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处。从classpath中读取过文件的人,都知道需要写一些读取流的方法,很是繁琐。最近使用IDEA在打出.这个符号的时候,一行代...

学习MVC之租房网站(十二)-缓存和静态页面

在上一篇<学习MVC之租房网站(十一)-定时任务和云存储>学习了Quartz的使用、发邮件,并将通过UEditor上传的图片保存到云存储。在项目的最后,再学习优化网站性能的一些技术:缓存和...

Linux系统下运行c++程序(linux怎么运行c++文件)

引言为什么要在Linux下写程序?需要更多关于Linux下c++开发的资料请后台私信【架构】获取分享资料包括:C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,fastdf...

2022正确的java学习顺序(文末送java福利)

对于刚学习java的人来说,可能最大的问题是不知道学习方向,每天学了什么第二天就忘了,而课堂的讲解也是很片面的。今天我结合我的学习路线为大家讲解下最基础的学习路线,真心希望能帮到迷茫的小伙伴。(有很多...

一个 3 年 Java 程序员 5 家大厂的面试总结(已拿Offer)

前言15年毕业到现在也近三年了,最近面试了阿里集团(菜鸟网络,蚂蚁金服),网易,滴滴,点我达,最终收到点我达,网易offer,蚂蚁金服二面挂掉,菜鸟网络一个月了还在流程中...最终有幸去了网易。但是要...

多商户商城系统开发全流程解析(多商户商城源码免费下载)

在数字化商业浪潮中,多商户商城系统成为众多企业拓展电商业务的关键选择。这类系统允许众多商家在同一平台销售商品,不仅丰富了商品种类,还为消费者带来更多样的购物体验。不过,开发一个多商户商城系统是个复杂的...

取消回复欢迎 发表评论: