JDK 17与Spring Boot 3环境下MyBatis-Plus整合实战与进阶配置

📅 2026/8/21 18:54:42
JDK 17与Spring Boot 3环境下MyBatis-Plus整合实战与进阶配置
最近在升级技术栈时很多同学都遇到了一个典型问题如何在最新的 JDK 17 和 Spring Boot 3 环境下高效、稳定地集成 MyBatis-Plus网上资料要么版本老旧要么配置零散特别是涉及到分页限制、枚举映射、字段加解密等进阶需求时踩坑无数。本文将为你提供一套从零开始、覆盖核心功能到生产级实践的完整解决方案包含可复制的代码、清晰的配置解释以及高频避坑指南。无论你是想在新项目中尝鲜最新技术栈还是为老项目升级铺路都能在这里找到答案。1. 背景与核心概念为什么是 JDK 17 Spring Boot 3 MyBatis-Plus在开始动手之前我们先理清这三个技术组合在一起的价值与挑战。JDK 17作为最新的长期支持LTS版本带来了诸多现代语言特性和性能提升如密封类Sealed Classes、模式匹配Pattern Matching等能帮助开发者写出更安全、更简洁的代码。越来越多的企业新项目开始将其作为基线版本。Spring Boot 3是一个重大升级版本其底层基于 Spring Framework 6并强制要求 JDK 17 作为最低版本。它提供了对 GraalVM 原生镜像的更好支持改进了 observability可观测性并引入了大量新的 starter 和配置属性。对于 MyBatis-Plus 用户而言最大的变化之一是 Spring Boot 3 移除了对javax包的支持全面转向jakarta包这直接影响了 MyBatis-Plus 等依赖 Servlet API 的组件。MyBatis-Plus简称 MP是国内最流行的 MyBatis 增强工具在 MyBatis 的基础上只做增强不做改变提供了通用的 Mapper、分页插件、代码生成器等功能极大地简化了 CRUD 操作。在 Spring Boot 3 环境下MyBatis-Plus 需要对应其 3.5.x 及以上版本才能完全兼容。这个技术栈组合的核心价值在于利用 JDK 17 的现代特性和性能依托 Spring Boot 3 强大的生态和新的能力再通过 MyBatis-Plus 提升数据层开发效率构建出高性能、易维护的后端服务。然而挑战也随之而来依赖版本兼容性、配置项变更、以及一些在旧版本中不存在的“新坑”如单页500条限制的默认行为。2. 环境准备与版本说明工欲善其事必先利其器。确保你的开发环境与以下版本对齐是避免后续一系列奇怪问题的第一步。核心环境清单操作系统Windows 10/11, macOS, Linux (本文演示环境为 Windows 11)JDKOracle JDK 17或OpenJDK 17(建议使用azul-17等发行版)IDEIntelliJ IDEA 2023.1 或更高版本 (对 JDK 17 和 Spring Boot 3 支持更好)构建工具Apache Maven 3.6 或 Gradle 7.x数据库MySQL 8.0 (本文以 MySQL 8.0.33 为例)关键依赖版本这是整个项目的基石版本不匹配是绝大多数启动失败和运行时异常的根源。!-- 在项目的 pom.xml 中 -- parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.2.5/version !-- 使用最新的 Spring Boot 3.2.x 系列 -- relativePath/ /parent properties java.version17/java.version !-- 必须使用 3.5.3 以兼容 Spring Boot 3 和 Jakarta -- mybatis-plus.version3.5.7/mybatis-plus.version /properties dependencies !-- Spring Boot Web 核心 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Spring Boot 数据访问核心 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-jdbc/artifactId /dependency !-- MySQL 驱动 -- dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency !-- MyBatis-Plus 核心依赖 -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version${mybatis-plus.version}/version /dependency !-- 代码生成器按需引入 -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-generator/artifactId version${mybatis-plus.version}/version scopeprovided/scope /dependency !-- Lombok 简化实体类 -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency !-- 测试 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency /dependencies版本选择核心逻辑Spring Boot 3.x强制要求 JDK 17。MyBatis-Plus在3.5.0版本开始提供了对jakarta包的支持因此必须使用3.5.3或更高版本以确保与 Spring Boot 3 的完全兼容。3.5.7是一个经过大量项目验证的稳定版本。MySQL 驱动使用 Spring Boot 父工程管理的版本即可它默认会引入兼容的版本。3. 核心配置与基础整合环境就绪后我们开始进行核心配置。这里会详细解释每一个配置项的作用而不仅仅是粘贴代码。3.1 数据库连接配置首先在application.yml(或application.properties) 中配置数据库连接。YAML 格式更清晰推荐使用。# application.yml spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver # 注意使用本地数据库请替换 localhost 和端口并确保数据库已创建 url: jdbc:mysql://localhost:3306/mybatis_plus_demo?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue username: root password: your_password # MyBatis-Plus 相关配置 mybatis-plus: configuration: # 控制台打印完整带参数 SQL 语句便于调试 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开启下划线转驼峰映射数据库字段 user_name 映射到实体属性 userName map-underscore-to-camel-case: true global-config: db-config: # 全局逻辑删除字段名需与实体类字段对应 logic-delete-field: deleted # 逻辑已删除值默认为 1 logic-delete-value: 1 # 逻辑未删除值默认为 0 logic-not-delete-value: 0 # 全局主键类型ASSIGN_ID雪花算法ASSIGN_UUIDAUTO数据库自增等 id-type: ASSIGN_ID配置项解读log-impl: 设置为StdOutImpl可以在控制台看到 MyBatis-Plus 执行的 SQL这是排查问题最有效的工具。生产环境请关闭。map-underscore-to-camel-case: 强烈建议开启这是 Java 实体类命名与数据库表字段命名的通用映射规则。logic-delete-field: 逻辑删除配置。配置后调用deleteById方法将执行 UPDATE 语句而非 DELETE是实现“软删除”的便捷方式。id-type:ASSIGN_ID是 MyBatis-Plus 默认的雪花算法生成 ID适合分布式系统。如果你的表主键是数据库自增 (AUTO_INCREMENT)则设置为AUTO。3.2 实体类与 Mapper 创建MyBatis-Plus 的核心思想之一是约定优于配置。我们通过注解来建立实体类与数据库表的映射。1. 创建实体类 (Entity)假设我们有一张用户表sys_user。// 文件路径src/main/java/com/example/demo/entity/User.java package com.example.demo.entity; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import java.time.LocalDateTime; Data // Lombok 注解自动生成 getter, setter, toString 等方法 TableName(sys_user) // 指定对应数据库表名如果类名与表名一致忽略大小写和下划线可省略 public class User { /** * 主键 * TableId 注解声明主键type IdType.ASSIGN_ID 对应配置的全局雪花算法 */ TableId(type IdType.ASSIGN_ID) private Long id; /** * 用户名 */ private String username; /** * 密码 */ private String password; /** * 邮箱 */ private String email; /** * 创建时间 * TableField 注解用于配置字段映射 * fill FieldFill.INSERT 表示在插入操作时自动填充 */ TableField(fill FieldFill.INSERT) private LocalDateTime createTime; /** * 更新时间 * fill FieldFill.INSERT_UPDATE 表示在插入和更新操作时自动填充 */ TableField(fill FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; /** * 逻辑删除标记 (0-未删除1-已删除) * TableLogic 注解声明逻辑删除字段 */ TableLogic private Integer deleted; }2. 创建 Mapper 接口Mapper 接口继承 MyBatis-Plus 提供的BaseMapper即可获得丰富的 CRUD 方法。// 文件路径src/main/java/com/example/demo/mapper/UserMapper.java package com.example.demo.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.demo.entity.User; import org.apache.ibatis.annotations.Mapper; Mapper // Spring 管理注解也可在主类上用 MapperScan 批量扫描 public interface UserMapper extends BaseMapperUser { // 无需编写任何方法BaseMapper 已提供了 insert, selectById, updateById, deleteById 等基础方法 // 可以在此定义自定义的复杂 SQL 查询方法 }3. 自动填充处理器为了自动填充createTime和updateTime我们需要实现MetaObjectHandler接口。// 文件路径src/main/java/com/example.demo/handler/MyMetaObjectHandler.java package com.example.demo.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 Bean 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()); // 如果需要也可以在这里填充其他固定值如创建人ID等 } Override public void updateFill(MetaObject metaObject) { // 更新时只为 updateTime 字段填充当前时间 this.strictUpdateFill(metaObject, updateTime, LocalDateTime.class, LocalDateTime.now()); } }3.3 主启动类与测试完成上述步骤后编写主启动类和简单的测试。// 文件路径src/main/java/com/example/demo/DemoApplication.java package com.example.demo; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; SpringBootApplication MapperScan(com.example.demo.mapper) // 扫描 Mapper 接口所在的包替代每个 Mapper 上的 Mapper 注解 public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }现在你可以运行DemoApplication如果控制台没有报错且看到 Spring Boot 启动成功的标志说明基础整合成功4. 核心功能实战与进阶基础整合只是开始MyBatis-Plus 的强大在于其开箱即用的高级功能。下面我们针对网络热词中提到的高频需求进行实战。4.1 解决“单页500条限制”问题这是 MyBatis-Plus 分页插件的一个默认安全限制防止有人误操作或恶意查询导致一次性拉取过多数据拖垮数据库。你需要自定义分页插件配置来修改或取消这个限制。1. 创建分页插件配置类// 文件路径src/main/java/com/example/demo/config/MybatisPlusConfig.java package com.example.demo.config; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); // 分页插件 PaginationInnerInterceptor paginationInnerInterceptor new PaginationInnerInterceptor(); // 设置数据库类型根据实际情况调整 paginationInnerInterceptor.setDbType(DbType.MYSQL); // 设置请求的页面大于最大页后操作true调回到首页false继续请求。默认false paginationInnerInterceptor.setOverflow(false); // 设置最大单页限制数量默认 500 条-1 表示不受限制生产环境慎用 paginationInnerInterceptor.setMaxLimit(1000L); // 例如设置为1000条 // 将分页插件添加到拦截器链中 interceptor.addInnerInterceptor(paginationInnerInterceptor); // 可以继续添加其他插件如乐观锁插件 // interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; } }2. 在 Service 中使用分页// 文件路径src/main/java/com/example/demo/service/impl/UserServiceImpl.java package com.example.demo.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.demo.entity.User; import com.example.demo.mapper.UserMapper; import com.example.demo.service.UserService; import org.springframework.stereotype.Service; Service public class UserServiceImpl extends ServiceImplUserMapper, User implements UserService { public PageUser getUserPage(Long current, Long size) { // 1. 创建分页对象参数当前页每页大小 PageUser page new Page(current, size); // 2. 创建查询条件可选 LambdaQueryWrapperUser wrapper new LambdaQueryWrapper(); wrapper.like(User::getUsername, 张); // 例如查询用户名包含“张”的用户 // 3. 执行分页查询 return baseMapper.selectPage(page, wrapper); // 返回的 Page 对象包含 records当前页数据列表、total总记录数、size、current 等信息 } }重要提醒setMaxLimit(-1L)可以取消限制但在生产环境中极其危险可能导致全表扫描和内存溢出。务必根据业务实际情况设置一个合理的上限。4.2 枚举类型映射将数据库的tinyint或varchar字段映射为 Java 枚举能极大提升代码的可读性和健壮性。MyBatis-Plus 提供了优雅的支持。1. 定义枚举类// 文件路径src/main/java/com/example/demo/enums/UserStatusEnum.java package com.example.demo.enums; import com.baomidou.mybatisplus.annotation.EnumValue; import lombok.Getter; Getter public enum UserStatusEnum { DISABLED(0, 禁用), ENABLED(1, 启用), LOCKED(2, 锁定); // EnumValue 注解标记存储在数据库中的字段值 EnumValue private final Integer code; private final String desc; UserStatusEnum(Integer code, String desc) { this.code code; this.desc desc; } }2. 在实体类中使用枚举字段// 在 User.java 实体类中添加字段 public class User { // ... 其他字段 private UserStatusEnum status; // 直接使用枚举类型 }3. 在application.yml中配置枚举处理器mybatis-plus: configuration: # ... 其他配置 default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler好处类型安全编译器会检查避免传入无效的整型或字符串。代码自解释user.setStatus(UserStatusEnum.ENABLED)比user.setStatus(1)清晰得多。方便序列化配合 Jackson 等 JSON 库可以方便地序列化为字面值或编码值。4.3 字段自动加解密拦截器对于密码、手机号、身份证号等敏感信息入库前加密、出库后解密是一个常见需求。我们可以通过实现 MyBatis-Plus 的TypeHandler或使用拦截器来实现。这里展示一个相对简单、基于自定义TypeHandler的 AES 加解密示例1. 添加加解密工具依赖以 Hutool 为例dependency groupIdcn.hutool/groupId artifactIdhutool-crypto/artifactId version5.8.25/version /dependency2. 创建加解密 TypeHandler// 文件路径src/main/java/com/example/demo/handler/EncryptTypeHandler.java package com.example.demo.handler; import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.symmetric.AES; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.MappedJdbcTypes; import org.apache.ibatis.type.MappedTypes; import java.nio.charset.StandardCharsets; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; // 声明处理的 JDBC 类型 MappedJdbcTypes(JdbcType.VARCHAR) // 声明处理的 Java 类型 MappedTypes(String.class) public class EncryptTypeHandler extends BaseTypeHandlerString { // 示例密钥实际项目应从安全配置中心获取 private static final byte[] KEY 1234567890123456.getBytes(StandardCharsets.UTF_8); private final AES aes SecureUtil.aes(KEY); Override public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException { // 写入数据库前加密 String encrypted aes.encryptHex(parameter); ps.setString(i, encrypted); } Override public String getNullableResult(ResultSet rs, String columnName) throws SQLException { String encrypted rs.getString(columnName); // 从数据库读取后解密 return encrypted ! null ? aes.decryptStr(encrypted) : null; } Override public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException { String encrypted rs.getString(columnIndex); return encrypted ! null ? aes.decryptStr(encrypted) : null; } Override public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { String encrypted cs.getString(columnIndex); return encrypted ! null ? aes.decryptStr(encrypted) : null; } }3. 在实体类字段上应用 TypeHandler// 在 User.java 实体类的 password 字段上添加注解 public class User { // ... TableField(typeHandler EncryptTypeHandler.class) private String password; // ... }现在当你使用userMapper.insert(user)时password字段会自动加密后存储使用userMapper.selectById(id)查询时会自动解密。注意此方法会影响基于该字段的模糊查询like因为数据库里存的是密文。4.4 代码自动生成器对于大型项目手动创建每个表的 Entity、Mapper、Service、Controller 非常繁琐。MyBatis-Plus 代码生成器可以一键完成。1. 编写一个简单的生成器类可作为一次性工具运行// 文件路径src/test/java/com/example/demo/CodeGenerator.java package com.example.demo; 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/mybatis_plus_demo?serverTimezoneAsia/Shanghai, root, your_password) .globalConfig(builder - { builder.author(YourName) // 设置作者 .outputDir(D://code-gen) // 指定输出目录绝对路径 .disableOpenDir() // 生成后不打开目录 .commentDate(yyyy-MM-dd); // 注释日期格式 }) .packageConfig(builder - { builder.parent(com.example.demo) // 设置父包名 .pathInfo(Collections.singletonMap(OutputFile.xml, D://code-gen//mapper-xml)); // 设置mapperXml生成路径 }) .strategyConfig(builder - { builder.addInclude(sys_user) // 设置需要生成的表名可多个 .addTablePrefix(sys_) // 设置过滤表前缀 .entityBuilder() // 实体类策略配置 .enableLombok() // 启用 Lombok .enableTableFieldAnnotation() // 生成字段注解 .controllerBuilder() // Controller策略配置 .enableRestStyle(); // 启用 RestController 风格 }) .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板默认的是Velocity引擎 .execute(); } }运行此main方法即可在指定目录生成全套代码。注意生成后需要将文件手动复制到项目对应目录并检查调整。5. 常见问题与排查思路在实际开发中你可能会遇到以下问题问题现象可能原因排查思路与解决方案启动报错java.lang.ClassNotFoundException: javax.servlet...Spring Boot 3 移除了javax.servlet改用jakarta.servlet。MyBatis-Plus 版本过低。确保 MyBatis-Plus 版本为3.5.3。检查所有相关依赖如分页插件、代码生成器版本是否一致。控制台不打印 SQL 日志1.application.yml中log-impl配置错误或未生效。2. 日志级别设置问题。1. 确认配置为org.apache.ibatis.logging.stdout.StdOutImpl。2. 在application.yml中增加logging.level.com.example.demo.mapper: DEBUG。分页查询结果 total 为 0但 records 有数据分页插件未正确配置或未注入 Spring 容器。检查MybatisPlusConfig配置类是否被Configuration注解且Bean方法正确。确认在 Service 中使用了Page对象进行查询。字段自动填充如 createTime不生效1. 实体类字段未加TableField(fill ...)注解。2.MetaObjectHandler实现类未加Component注解。3. 使用的是update()方法而非updateById()。1. 检查注解。2. 检查处理器是否被扫描。3. 自动填充仅对insert(),updateById()等明确的方法生效update(wrapper)需要额外处理。枚举字段存入数据库的是枚举名而非 code 值未在枚举的 code 字段上加EnumValue注解或未配置default-enum-type-handler。1. 确认EnumValue注解加在正确的字段上。2. 在application.yml中配置default-enum-type-handler。使用logic-delete后查询仍然包含已删除数据1. 实体类逻辑删除字段未加TableLogic。2. 全局配置中的logic-delete-field值与实体类字段名不一致。1. 实体类字段加TableLogic。2. 确保全局配置与实体类字段名一致或直接在TableLogic中指定值。ID 生成不是雪花算法全局id-type配置未生效或实体类TableId注解指定了其他类型。1. 检查application.yml中mybatis-plus.global-config.db-config.id-type。2. 检查实体类TableId的type属性是否覆盖了全局配置。6. 最佳实践与工程建议将 MyBatis-Plus 用于生产级项目除了功能实现更需要注意代码质量和工程规范。统一依赖管理在父工程或核心模块的pom.xml中使用dependencyManagement统一管理所有 MyBatis-Plus 相关依赖的版本避免冲突。分离环境配置使用application-dev.yml,application-prod.yml区分不同环境的数据库连接、日志级别等。生产环境务必关闭log-impl的StdOutImpl改为使用文件或 Logback 等日志框架。善用 Service 层封装不要直接在 Controller 中调用BaseMapper的方法。应创建对应的Service接口和实现类并继承ServiceImpl。在 Service 层进行业务逻辑封装、事务管理。Wrapper 使用规范使用LambdaQueryWrapper和LambdaUpdateWrapper避免魔法值Magic String编译时安全。复杂的查询条件建议封装成独立的查询对象如UserQuery在 Service 中构建 Wrapper。分页查询优化对于数据量巨大的表单纯使用LIMIT offset, size在深度分页时性能极差。考虑使用基于主键或索引列的“上一页/下一页”查询方式或者使用 MyBatis-Plus 的Page对象结合自定义count查询对于多表关联复杂查询。枚举映射的序列化如果你的 API 需要返回枚举字段默认 Jackson 会序列化成枚举对象如{code:1, desc:启用}。如果想返回code或desc可以在枚举字段的 getter 方法上使用JsonValue或在实体类上使用JsonSerialize。敏感信息处理加解密密钥绝不能硬编码在代码中。应使用配置中心如 Apollo、Nacos或环境变量注入。加解密TypeHandler可以考虑结合 Spring 的Value注解来获取密钥。代码生成器的使用代码生成器仅用于项目初期搭建骨架或增加新表时快速生成基础代码。生成后一定要仔细检查并根据业务需求进行定制化修改切勿直接覆盖已有业务逻辑。7. 总结通过本文的梳理我们完成了在 JDK 17 和 Spring Boot 3 环境下整合 MyBatis-Plus 的全流程涵盖了从环境搭建、基础 CRUD、到分页限制、枚举映射、字段加解密等高级特性的实战。关键在于把握版本兼容性MP 3.5.3理解核心配置项的含义并遵循一定的工程实践来保证代码质量。下一步你可以继续探索 MyBatis-Plus 的更多特性如多数据源动态切换DS注解、SQL 注入器自定义全局方法、性能分析插件等以应对更复杂的业务场景。同时结合 Spring Boot 3 的特性如 Actuator 端点监控、Micrometer 指标收集可以构建出更健壮、更易观测的现代化后端服务。