java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > MyBatis-Plus Spring Boot 集成

MyBatis-Plus 全面介绍与Spring Boot 集成最佳实践

作者:阿拉斯攀登

MyBatis-Plus是MyBatis的增强工具,提供了通用CRUD、分页、条件构造器等功能,简化了开发,减少重复SQL编写,它支持多种主键生成策略,内置乐观锁和逻辑删除,且兼容MyBatis原有代码,本文介绍MyBatis-Plus全面介绍与Spring Boot 集成最佳实践,感兴趣的朋友一起看看吧

MyBatis-Plus(简称 MP)是基于 MyBatis 的增强工具,在 MyBatis 基础上只做增强不做改变,提供了大量开箱即用的功能(如通用 CRUD、分页、条件构造器等),彻底简化了 MyBatis 的开发,大幅减少重复的 SQL 编写工作。

一、MyBatis-Plus 核心优势

特性说明
通用 CRUD内置 BaseMapper/IService,无需编写基础增删改查 SQL
条件构造器链式编程构建动态 SQL,替代 MyBatis 繁琐的 XML 动态标签
自动分页内置分页插件,无需集成 PageHelper,分页查询更简单
代码生成器一键生成 Entity、Mapper、Service、Controller 全套代码
主键生成策略支持雪花算法、自增、UUID 等多种主键生成方式,无需手动配置
逻辑删除自动处理逻辑删除(标记删除),无需手动写删除 SQL
乐观锁内置乐观锁插件,解决并发更新问题
多数据源简单配置即可实现多数据源切换,无需复杂的代码改造
注解式开发支持注解替代 XML,同时兼容 MyBatis 原有 XML 写法

二、MyBatis-Plus 核心组件

1. 核心接口 / 类

2. 核心注解

注解作用
@TableName映射数据库表名(实体类名与表名不一致时使用)
@TableId标记主键字段,指定主键生成策略(如 IdType.ASSIGN_ID 雪花算法)
@TableField映射数据库字段(字段名与属性名不一致、忽略字段、填充字段等)
@TableLogic标记逻辑删除字段(如 deleted=0 未删除,deleted=1 已删除)
@Version标记乐观锁字段(用于并发更新控制)
@TableFill标记自动填充字段(如创建时间、更新时间)

三、Spring Boot 集成 MyBatis-Plus 实战

1. 环境准备

(1)依赖引入(Maven)

<!-- Spring Boot 父工程 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.0</version>
    <relativePath/>
</parent>
<dependencies>
    <!-- MyBatis-Plus 核心依赖 -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.5</version> <!-- 最新稳定版 -->
    </dependency>
    <!-- MySQL 驱动 -->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>
    <!-- Lombok(简化实体类) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <!-- Spring Boot 测试 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

(2)核心配置(application.yml)

# 数据源配置
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mp_demo?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8
    username: root
    password: 123456
    # 连接池配置(默认 HikariCP)
    hikari:
      maximum-pool-size: 10
      minimum-idle: 5
# MyBatis-Plus 配置
mybatis-plus:
  # 实体类别名包
  type-aliases-package: com.example.mp.entity
  # Mapper XML 文件位置(如需保留 XML 写法)
  mapper-locations: classpath:mapper/**/*.xml
  # 全局配置
  global-config:
    db-config:
      # 主键生成策略:雪花算法(分布式唯一ID)
      id-type: assign_id
      # 逻辑删除配置
      logic-delete-field: deleted # 逻辑删除字段名
      logic-delete-value: 1       # 已删除值
      logic-not-delete-value: 0   # 未删除值
  # 配置项(兼容 MyBatis 配置)
  configuration:
    map-underscore-to-camel-case: true # 自动下划线转驼峰(默认开启)
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印 SQL 日志

2. 代码实现

(1)数据库表设计

CREATE DATABASE IF NOT EXISTS mp_demo;
USE mp_demo;
-- 用户表(含逻辑删除、自动填充字段)
CREATE TABLE `user` (
  `id` BIGINT NOT NULL COMMENT '主键ID(雪花算法)',
  `user_name` VARCHAR(50) NOT NULL COMMENT '用户名',
  `age` INT COMMENT '年龄',
  `email` VARCHAR(100) COMMENT '邮箱',
  `deleted` TINYINT DEFAULT 0 COMMENT '逻辑删除(0-未删,1-已删)',
  `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`)
) COMMENT '用户表';

(2)实体类(Entity)

package com.example.mp.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("user") // 表名映射(实体名与表名一致时可省略)
public class User {
    // 主键:雪花算法生成(IdType.ASSIGN_ID)
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
    // 字段映射(属性名与字段名一致时可省略)
    @TableField("user_name")
    private String userName;
    private Integer age;
    private String email;
    // 逻辑删除字段
    @TableLogic
    private Integer deleted;
    // 自动填充字段:创建时间
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    // 自动填充字段:更新时间
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
}

(3)自定义字段填充处理器(处理 createTime/updateTime)

package com.example.mp.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component // 必须交给 Spring 管理
public class MyMetaObjectHandler implements MetaObjectHandler {
    // 插入时填充
    @Override
    public void insertFill(MetaObject metaObject) {
        // 填充 createTime 和 updateTime
        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
        this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }
    // 更新时填充
    @Override
    public void updateFill(MetaObject metaObject) {
        // 只填充 updateTime
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }
}

(4)Mapper 接口(继承 BaseMapper)

package com.example.mp.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.mp.entity.User;
import org.apache.ibatis.annotations.Mapper;
// 无需编写任何方法,BaseMapper 已封装所有基础 CRUD
@Mapper // 或在启动类加 @MapperScan("com.example.mp.mapper")
public interface UserMapper extends BaseMapper<User> {
    // 如需自定义 SQL,可在此编写(兼容 MyBatis 注解/XML 方式)
    // 示例:自定义查询
    // @Select("SELECT * FROM user WHERE user_name = #{userName}")
    // List<User> selectByUserName(String userName);
}

(5)Service 层(通用 Service)

① Service 接口(继承 IService)

package com.example.mp.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.mp.entity.User;
public interface UserService extends IService<User> {
    // 如需自定义业务方法,在此扩展
}

② Service 实现类(继承 ServiceImpl)

package com.example.mp.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.mp.entity.User;
import com.example.mp.mapper.UserMapper;
import com.example.mp.service.UserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    // 通用 Service 方法已全部实现,无需编写代码
}

(6)启动类

package com.example.mp;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// 批量扫描 Mapper 接口(替代每个 Mapper 加 @Mapper)
@MapperScan("com.example.mp.mapper")
@SpringBootApplication
public class MpDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(MpDemoApplication.class, args);
    }
}

3. 核心功能使用示例

(1)通用 CRUD(Mapper 层)

package com.example.mp;
import com.example.mp.entity.User;
import com.example.mp.mapper.UserMapper;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
public class MapperCrudTest {
    @Resource
    private UserMapper userMapper;
    // 新增
    @Test
    public void testInsert() {
        User user = new User();
        user.setUserName("张三");
        user.setAge(25);
        user.setEmail("zhangsan@example.com");
        // insert 方法返回受影响行数
        int rows = userMapper.insert(user);
        System.out.println("新增行数:" + rows + ",用户ID:" + user.getId()); // 雪花算法生成ID
    }
    // 根据ID查询
    @Test
    public void testSelectById() {
        User user = userMapper.selectById(176928745896704L);
        System.out.println("查询结果:" + user);
    }
    // 查询所有
    @Test
    public void testSelectList() {
        List<User> userList = userMapper.selectList(null); // null 表示无条件
        userList.forEach(System.out::println);
    }
    // 根据ID更新
    @Test
    public void testUpdateById() {
        User user = new User();
        user.setId(176928745896704L);
        user.setAge(26); // 只更新age字段
        int rows = userMapper.updateById(user);
        System.out.println("更新行数:" + rows);
    }
    // 根据ID删除(逻辑删除)
    @Test
    public void testDeleteById() {
        int rows = userMapper.deleteById(176928745896704L);
        System.out.println("删除行数:" + rows); // 实际执行 UPDATE user SET deleted=1 WHERE id=?
    }
}

(2)条件构造器(QueryWrapper/LambdaQueryWrapper)

@Test
public void testQueryWrapper() {
    // 1. 普通 QueryWrapper(需手动写字段名,易出错)
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("age", 25) // 等于
                .like("user_name", "张") // 模糊查询
                .orderByDesc("create_time"); // 排序
    List<User> userList = userMapper.selectList(queryWrapper);
    System.out.println("普通条件查询:" + userList);
    // 2. LambdaQueryWrapper(类型安全,推荐)
    LambdaQueryWrapper<User> lambdaQuery = new LambdaQueryWrapper<>();
    lambdaQuery.eq(User::getAge, 25)
               .like(User::getUserName, "张")
               .orderByDesc(User::getCreateTime);
    List<User> lambdaList = userMapper.selectList(lambdaQuery);
    System.out.println("Lambda条件查询:" + lambdaList);
}

(3)自动分页查询

@Test
public void testPage() {
    // 1. 构建分页对象(第1页,每页10条)
    Page<User> page = new Page<>(1, 10);
    // 2. 分页查询(条件为null表示无条件)
    Page<User> resultPage = userMapper.selectPage(page, null);
    // 3. 分页结果解析
    System.out.println("总条数:" + resultPage.getTotal());
    System.out.println("总页数:" + resultPage.getPages());
    System.out.println("当前页数据:" + resultPage.getRecords());
    System.out.println("当前页码:" + resultPage.getCurrent());
    System.out.println("每页条数:" + resultPage.getSize());
}
// 自定义SQL分页(如需自定义查询)
// 1. Mapper 接口添加方法
// Page<User> selectUserPage(Page<User> page, @Param("ew") LambdaQueryWrapper<User> queryWrapper);
// 2. XML 编写 SQL(无需写 LIMIT,MP 自动拼接)
// <select id="selectUserPage" resultType="User">
//     SELECT * FROM user ${ew.customSqlSegment}
// </select>
// 3. 调用
// Page<User> page = new Page<>(1, 10);
// LambdaQueryWrapper<User> query = new LambdaQueryWrapper<>().gt(User::getAge, 20);
// Page<User> result = userMapper.selectUserPage(page, query);

(4)通用 Service 层使用(批量操作、分页)

package com.example.mp;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.mp.entity.User;
import com.example.mp.service.UserService;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Arrays;
import java.util.List;
@SpringBootTest
public class ServiceCrudTest {
    @Resource
    private UserService userService;
    // 批量新增
    @Test
    public void testSaveBatch() {
        User user1 = new User();
        user1.setUserName("李四");
        user1.setAge(24);
        User user2 = new User();
        user2.setUserName("王五");
        user2.setAge(26);
        boolean success = userService.saveBatch(Arrays.asList(user1, user2));
        System.out.println("批量新增是否成功:" + success);
    }
    // 分页查询(Service 层)
    @Test
    public void testServicePage() {
        Page<User> page = new Page<>(1, 10);
        LambdaQueryWrapper<User> query = new LambdaQueryWrapper<>().gt(User::getAge, 20);
        IPage<User> resultPage = userService.page(page, query);
        System.out.println("Service分页总条数:" + resultPage.getTotal());
    }
    // 批量删除
    @Test
    public void testRemoveBatchByIds() {
        boolean success = userService.removeBatchByIds(Arrays.asList(176928745896704L, 176928745896705L));
        System.out.println("批量删除是否成功:" + success);
    }
    // 条件查询单个结果
    @Test
    public void testGetOne() {
        LambdaQueryWrapper<User> query = new LambdaQueryWrapper<>().eq(User::getUserName, "张三");
        User user = userService.getOne(query, false); // false 表示不抛出多条结果异常
        System.out.println("单个结果:" + user);
    }
}

四、MyBatis-Plus 高级功能

1. 乐观锁插件(解决并发更新)

(1)添加乐观锁字段

ALTER TABLE `user` ADD COLUMN `version` INT DEFAULT 1 COMMENT '乐观锁版本号';

(2)实体类添加注解

@Version // 标记乐观锁字段
private Integer version;

(3)配置乐观锁插件

package com.example.mp.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
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());
        // 乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
}

(4)使用示例

@Test
public void testOptimisticLocker() {
    // 1. 查询用户(获取当前版本号)
    User user = userMapper.selectById(176928745896704L);
    // 2. 更新用户
    user.setAge(27);
    int rows = userMapper.updateById(user);
    // 并发场景下,若版本号不匹配,rows=0(更新失败)
    System.out.println("更新行数:" + rows);
}

2. 代码生成器(AutoGenerator)

MP 提供了强大的代码生成器,可一键生成全套代码,减少重复劳动。

(1)引入代码生成器依赖

<!-- MyBatis-Plus 代码生成器 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.5.5</version>
</dependency>
<!-- 模板引擎(默认 Velocity) -->
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-engine-core</artifactId>
    <version>2.3</version>
</dependency>

(2)生成代码示例

package com.example.mp;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import java.util.Collections;
public class CodeGenerator {
    public static void main(String[] args) {
        // 数据源配置
        String url = "jdbc:mysql://localhost:3306/mp_demo?useSSL=false&serverTimezone=Asia/Shanghai";
        String username = "root";
        String password = "123456";
        // 快速生成代码
        FastAutoGenerator.create(url, username, password)
                // 全局配置
                .globalConfig(builder -> {
                    builder.author("mp-demo") // 设置作者
                            .outputDir(System.getProperty("user.dir") + "/src/main/java") // 输出目录
                            .enableSwagger() // 开启 Swagger(如需)
                            .commentDate("yyyy-MM-dd") // 注释日期格式
                            .disableOpenDir(); // 生成后不打开目录
                })
                // 包配置
                .packageConfig(builder -> {
                    builder.parent("com.example.mp") // 父包名
                            .moduleName("") // 模块名(无则空)
                            .entity("entity") // 实体类包名
                            .mapper("mapper") // Mapper 包名
                            .service("service") // Service 包名
                            .controller("controller") // Controller 包名
                            .pathInfo(Collections.singletonMap(OutputFile.mapperXml, 
                                    System.getProperty("user.dir") + "/src/main/resources/mapper")); // Mapper XML 路径
                })
                // 策略配置
                .strategyConfig(builder -> {
                    builder.addInclude("user") // 生成指定表(多个表用逗号分隔)
                            .addTablePrefix("t_", "sys_") // 过滤表前缀(如 t_user → User)
                            // 实体类策略
                            .entityBuilder()
                                .enableLombok() // 开启 Lombok
                                .enableTableFieldAnnotation() // 生成字段注解
                                .idType(com.baomidou.mybatisplus.annotation.IdType.ASSIGN_ID) // 主键策略
                            // Mapper 策略
                            .mapperBuilder()
                                .enableBaseResultMap() // 生成 BaseResultMap
                                .enableBaseColumnList() // 生成 BaseColumnList
                            // Service 策略
                            .serviceBuilder()
                                .formatServiceFileName("%sService") // Service 命名格式
                                .formatServiceImplFileName("%sServiceImpl") // ServiceImpl 命名格式
                            // Controller 策略
                            .controllerBuilder()
                                .enableRestStyle(); // 开启 RestController 风格
                })
                // 执行生成
                .execute();
    }
}

3. 多数据源配置

MP 支持简单配置实现多数据源,适用于读写分离、分库等场景。

(1)引入多数据源依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.6.1</version>
</dependency>

(2)配置多数据源(application.yml)

spring:
  datasource:
    dynamic:
      primary: master # 默认数据源
      strict: false # 关闭严格模式
      datasource:
        # 主库
        master:
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://localhost:3306/mp_demo_master?useSSL=false
          username: root
          password: 123456
        # 从库
        slave:
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://localhost:3306/mp_demo_slave?useSSL=false
          username: root
          password: 123456

(3)使用多数据源(注解指定)

// Service 层指定数据源
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    // 主库(默认)
    public List<User> selectMaster() {
        return baseMapper.selectList(null);
    }
    // 从库
    @DS("slave") // 指定从库数据源
    public List<User> selectSlave() {
        return baseMapper.selectList(null);
    }
}

五、MyBatis-Plus 与 MyBatis 对比

维度MyBatisMyBatis-Plus
基础 CRUD需手动编写 XML / 注解 SQL继承 BaseMapper/IService 直接用
动态 SQLXML 标签(if/where/foreach)条件构造器(链式编程)
分页需集成 PageHelper内置分页插件,一键分页
主键生成需手动配置内置多种策略(雪花算法 / 自增等)
逻辑删除需手动写 SQL注解 + 自动处理
代码生成无原生支持一键生成全套代码
兼容性原生 MyBatis 语法完全兼容 MyBatis,可混用 XML / 注解

六、最佳实践

  1. 优先使用 Lambda 条件构造器:避免硬编码字段名,减少拼写错误。
  2. 通用 CRUD 用 MP,复杂 SQL 用 XML:简单操作依赖 MP 通用方法,复杂关联查询仍用 MyBatis XML 更清晰。
  3. 合理使用逻辑删除:避免物理删除数据,便于数据恢复和审计。
  4. 乐观锁用于并发更新:如库存扣减、订单状态更新等场景。
  5. 代码生成器提升效率:新项目初始化时,用代码生成器一键生成基础代码。
  6. 分页查询必用 Page 对象:避免手动拼接 LIMIT,MP 自动适配不同数据库方言。
  7. 字段自动填充统一处理:创建时间、更新时间等字段通过填充处理器统一管理,避免重复代码。

七、总结

MyBatis-Plus 是 MyBatis 的 “黄金搭档”,在保留 MyBatis 灵活性的同时,大幅降低了开发成本:

掌握 MyBatis-Plus 的关键是理解通用 CRUD 接口条件构造器,在此基础上结合分页、乐观锁、代码生成器等高级功能,可极大提升开发效率。

到此这篇关于MyBatis-Plus 全面介绍与Spring Boot 集成最佳实践的文章就介绍到这了,更多相关MyBatis-Plus Spring Boot 集成内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文