ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

MyBatis 通用 Mapper 6 实战指南:JDK 17 + Jakarta JPA + Spring Boot 4 下的单表 CRUD 利器

MyBatis 通用 Mapper 6 实战指南:JDK 17 + Jakarta JPA + Spring Boot 4 下的单表 CRUD 利器
  • 后端
  • ORM
  • 数据库

【免费下载链接】Mapper

Mybatis Common Mapper - Easy to use

项目地址:https://gitcode.com/gh_mirrors/ma/Mapper
点击查看免费下载

本指南围绕 tk.mybatis 通用 Mapper 6.0.0(仓库根目录 README.md)展开,系统讲解其版本选型、依赖引入、通用方法体系、Spring Boot 集成与全局配置,并结合仓库源码(core、base、extra 等模块)揭示其 SQL 自动生成的底层原理。读完本文,你将能够在 JDK 17 与 Spring Boot 4 项目中快速引入通用 Mapper,用极少的代码完成单表增删改查,并能按需设计属于自己的通用方法。

为什么需要通用 Mapper

传统 MyBatis 开发中,每张表都要手写实体类、Mapper 接口与 XML(或注解)SQL,selectByPrimaryKey、insert、updateByPrimaryKey、deleteByPrimaryKey这类样板代码重复度极高。通用 Mapper 解决的正是在此问题:它把单表 CRUD 中"套路化"的方法预先定义在通用接口中,由框架根据实体类上的 JPA 注解(@Table、@Id、@Column等)在运行时自动生成对应 SQL。

需要注意的是,仓库 README 明确说明了其能力边界:

  • 极其方便地使用 MyBatis 单表的增删改查;
  • 支持单表操作,不支持通用的多表联合查询。

多表查询仍应使用手写 SQL(XML 或注解)方式完成。同时,通用 Mapper 允许你"随意的按照自己的需要选择通用方法",也"可以很方便的开发自己的通用方法",这是其可扩展性设计的核心。

版本与分支选择

通用 Mapper 6 与不同 Spring Boot 大版本严格对应,请根据项目实际情况选择对应分支。以下是仓库 README.md 中给出的版本匹配表:

分支Spring Boot 版本JDK 版本Mapper 版本
masterSpring Boot 4.xJDK 17+6.0.0+
5.xSpring Boot 3.xJDK 17+5.0.0
4.3.xSpring Boot 2.xJDK 8+4.3.x

从仓库根 pom.xml 可以看到 6.0.0 版本的关键依赖与编译约束:

  • maven.compiler.source/target均为 17;
  • jakarta.persistence-api版本为 3.2.0(Jakarta JPA 注解);
  • mybatis版本为 3.5.19;
  • 聚合模块mapper-modules下包含all、base、core、extra、spring、weekend、generator、spring-boot-starter、solon-plugin等子模块。

引入 Maven 依赖

在 Spring Boot 4.x / JDK 17 项目中使用 6.0.0,直接在pom.xml中引入:

<dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper</artifactId> <version>6.0.0</version> </dependency>

该坐标对应 all/mapper 聚合产物,内部整合了核心(core)、基础方法(base)、扩展方法(extra)与 Spring 集成(spring)等能力。若只需基础能力,也可按需引入mapper-core、mapper-base、mapper-extra等独立坐标(见 all/pom.xml 中的dependencyManagement声明)。

若使用 Spring Boot 2.x / JDK 8,请选择 4.3.x 分支;Spring Boot 3.x / JDK 17 请选择 5.x 分支。

6.0 的核心变化:JDK 17 + Jakarta JPA 注解 + Spring Boot 4

6.0 版本的技术栈底座发生了根本性变化,README.md 对此说明如下:

  • 基于JDK 17 + Jakarta JPA 注解 + Spring Boot 4;
  • 配置完全兼容,但实体类需要改用新版本的 JPA 注解(jakarta.persistence.*,如jakarta.persistence.Id、jakarta.persistence.Table);
  • 同步更新(copy)了mybatis-spring 4.0.0与mybatis-spring-boot-starter 4.0.0;
  • 此次更新主要是依赖的更新,目标是适配最新的 Spring Boot 4.0.2,提供更好的兼容性和稳定性。

这一变化在仓库测试代码中有直接印证。例如 base 测试实体 Country 中主键注解已使用:

import jakarta.persistence.Id; public class Country implements Serializable { @Id private Long id; private String countryname; private String countrycode; ... }

如果你从旧版(javax.persistence.*)迁移,需要同步替换实体类中的 JPA 注解包名,业务配置代码则无需改动。

快速上手:第一个通用 Mapper

参考 base 模块测试,一个最小可用的通用 Mapper 只需三步。

第一步:定义实体类

使用 JPA 注解标注主键与表映射(表名、列名默认由实体类名/属性名按驼峰转下划线规则推导):

import jakarta.persistence.Id; public class Country implements Serializable { @Id private Long id; private String countryname; private String countrycode; // getter / setter 省略 }

第二步:继承通用 Mapper 接口

import tk.mybatis.mapper.common.Mapper; public interface CountryMapper extends Mapper<Country> { }

CountryMapper本身是空接口,但继承了 Mapper 后即获得一整套通用方法。查看源码可见其继承结构:

@RegisterMapper public interface Mapper<T> extends BaseMapper<T>, ExampleMapper<T>, RowBoundsMapper<T>, SaveMapper<T>, Marker { }

而 BaseMapper 再聚合了查询、插入、更新、删除四组基础接口:

@RegisterMapper public interface BaseMapper<T> extends BaseSelectMapper<T>, BaseInsertMapper<T>, BaseUpdateMapper<T>, BaseDeleteMapper<T> { }

第三步:注册扫描并在业务中使用

在 MyBatis 配置中注册该 Mapper(base 测试 mybatis-config.xml 的注册方式):

<mappers> <mapper class="tk.mybatis.mapper.base.CountryMapper"/> </mappers>

同时使用 MapperHelper 处理 Configuration,通用 Mapper 会将ProviderSqlSource替换为真实 SQL(测试基类 BaseTest 中mapperHelper.processConfiguration(...)即演示了非 Spring 环境下的接入方式)。

接入完成后即可直接调用通用方法:

CountryMapper mapper = sqlSession.getMapper(CountryMapper.class); Country country = mapper.selectByPrimaryKey(1L); // 按主键查询 mapper.insert(country); // 插入 mapper.updateByPrimaryKey(country); // 按主键更新 mapper.deleteByPrimaryKey(1L); // 按主键删除

通用方法全景

仓库 base 与 core 模块将通用方法按功能拆分为细粒度接口,便于"按需选择"。从源码结构看,主要分组如下:

分组接口代表方法
基础查询SelectMapper、SelectByPrimaryKeyMapper、SelectAllMapper、SelectOneMapper、SelectCountMapper、ExistsWithPrimaryKeyMapperselect、selectByPrimaryKey、selectAll、selectOne、selectCount、existsWithPrimaryKey
基础插入InsertMapper、InsertSelectiveMapperinsert、insertSelective
基础更新UpdateByPrimaryKeyMapper、UpdateByPrimaryKeySelectiveMapperupdateByPrimaryKey、updateByPrimaryKeySelective
基础删除DeleteByPrimaryKeyMapper、DeleteMapperdeleteByPrimaryKey、delete
条件查询SelectByConditionMapper、SelectCountByConditionMapper、DeleteByConditionMapper、UpdateByConditionMapper、UpdateByConditionSelectiveMapperselectByCondition、selectCountByCondition等
ExampleSelectByExampleMapper、SelectCountByExampleMapper、SelectOneByExampleMapper、DeleteByExampleMapper、UpdateByExampleMapper、UpdateByExampleSelectiveMapperselectByExample、deleteByExample等
Ids 批量SelectByIdsMapper、DeleteByIdsMapperselectByIds、deleteByIds
RowBounds 分页SelectRowBoundsMapper、SelectByExampleRowBoundsMapper、SelectByConditionRowBoundsMapperselectByRowBounds等
特殊InsertListMapper、InsertUseGeneratedKeysMapper批量插入、useGeneratedKeys插入
SQL Server 方言SqlServerMapper下InsertMapper、InsertSelectiveMapperSQL Server 方言插入

以 SelectByExampleMapper 为例,每个方法通过 MyBatis 的@SelectProvider注解绑定到 Provider 类:

@SelectProvider(type = ExampleProvider.class, method = "dynamicSQL") List<T> selectByExample(Object example);

dynamicSQL是 MapperTemplate 定义的占位方法,真正的 SQL 由框架在启动阶段生成并替换。

组合接口:按需拼装

仓库提供了多种预置组合接口,避免逐一继承细粒度接口:

  • BaseMapper<T>:基础增删改查;
  • Mapper<T>:BaseMapper + ExampleMapper + RowBoundsMapper + SaveMapper;
  • MySqlMapper<T>:追加 MySQL 相关方法(如批量插入);
  • SqlServerMapper<T>、ConditionMapper<T>、ExampleMapper<T>、IdsMapper<T>、SaveMapper<T>等。

这些接口统一标注了@RegisterMapper(见 RegisterMapper.java),会被 MapperHelper 自动识别注册。

深入原理:SQL 是如何自动生成的

通用 Mapper 的核心类是 MapperHelper,其注释直接标明"处理主要逻辑,最关键的一个类"。关键链路如下:

  1. 注册通用接口:registerMapper(Class<?>)遍历接口中声明的方法,读取方法上的@SelectProvider/@InsertProvider/@DeleteProvider/@UpdateProvider注解,找到对应的 Provider 模板类,并通过反射实例化 MapperTemplate 子类(如BaseSelectProvider、ExampleProvider)。若注解缺失或模板类非法,则回退到EmptyProvider。
  2. 方法注册:addMethodMap(methodName, method)将每个通用方法名绑定到模板类中同名方法,供后续 SQL 生成调用。
  3. 自动注册继承接口:registerMapper会递归注册当前接口的所有父接口,因此CountryMapper extends Mapper<T>时,Mapper、BaseMapper以及更细粒度的接口会被依次注册。
  4. 处理 MappedStatement:processConfiguration(Configuration)遍历 MyBatisConfiguration中全部MappedStatement,对命中的通用方法调用setSqlSource,将占位ProviderSqlSource替换为基于 XML 动态 SQL(XMLLanguageDriver)构建的真实SqlSource——参见 MapperTemplate.createSqlSource。
  5. 实体解析:SQL 生成依赖 EntityHelper 与 DefaultEntityResolve 解析实体类注解,得到表名、列名、主键、属性映射等元数据,缓存为EntityTable。

通过@RegisterMapper注解,框架还支持"继承的通用接口"自动注册(hasRegisterMapper递归检查父接口),这正是细粒度接口组合能力的来源。

全局配置详解

通用 Mapper 的全部可配置项集中定义在 Config.java,前缀为mapper(PREFIX = "mapper")。以下参数均可在application.properties/application.yml中通过mapper.xxx配置:

配置项默认值说明
mappers空(Spring Boot 下自动注册tk.mybatis.mapper.common.Mapper)逗号分隔的通用 Mapper 接口全限定类名列表
IDENTITY/identityMySQL主键自增回写 SQL,支持IdentityDialect内置方言(如 MYSQL),也可自定义
order/beforeAFTERSelectKey 回写主键的执行顺序,BEFORE或AFTER
seqFormat{0}.nextval序列获取规则,{0}为 SequenceName,针对 Oracle
catalog空全局 catalog,设置后 SQL 形如catalog.tablename
schema空全局 schema,与 catalog 同时设置时优先 catalog
stylecamelhump字段名转换风格:驼峰转下划线(camelhump)、normal、uppercase、lowercase等,非法值会抛出MapperException
notEmptyfalse对getAllIfColumnNode是否额外判断!= ''
useSimpleTypetrue(3.5.0 后)是否使用简单类型
enumAsSimpleTypefalse枚举是否作为简单类型处理
simpleTypes空注册新的基本类型,逗号分隔全限定类名
usePrimitiveTypefalse是否注册 8 种 Java 基本类型
enableMethodAnnotationfalse是否支持方法上的注解
checkExampleEntityClassfalse校验调用 Example 方法时Example(entityClass)与Mapper<EntityClass>是否一致
wrapKeyword空关键字包装,mysql 可设为`{0}`,sqlserver 可设为[{0}],{0}代表列名
resolveClass空自定义实体解析器(EntityResolve实现类)
safeDeletefalse安全删除:开启后不允许无条件的全表删除(如delete from table)
safeUpdatefalse安全更新:开启后不允许无条件的全表更新
logicDeleteByKeytrue通过主键进行 select/update/delete 时是否拼接逻辑删除条件,设为 false 时不追加logicDeleteField = 0
useJavaTypefalse是否在 SQL 参数中设置 javaType,如{id, javaType=java.lang.Long}
enableBaseResultMapFlagfalse是否自动把 Mapper 接口中手写指定 resultType 的查询结果改为 resultMap

这些参数的解析逻辑均可在 Config.setProperties 中找到,例如IDENTITY会先尝试通过IdentityDialect.getDatabaseDialect识别内置方言,识别失败则按自定义 SQL 处理;style非法时直接抛出异常。

Spring Boot 集成

仓库 spring-boot-starter 模块提供了mapper-spring-boot-starter与mapper-spring-boot-autoconfigure。其中 MapperProperties 直接继承Config并标注@ConfigurationProperties(prefix = "mapper"),其设计目的(见类注释)就是方便 IDE 自动提示mapper.开头的配置:

# 显式注册通用 Mapper 接口(不配置时默认自动注册 tk.mybatis.mapper.common.Mapper) mapper.mappers[0]=tk.mybatis.mapper.common.Mapper # 全局配置示例 mapper.style=camelhump mapper.safeDelete=true mapper.wrapKeyword=`{0}`

MapperAutoConfiguration 负责自动装配SqlSessionFactory与SqlSessionTemplate,并在初始化阶段将MapperProperties绑定到MapperHelper。在 Spring Boot 项目中,通常只需:

  1. 引入mapper-spring-boot-starter;
  2. 实体类与 Mapper 接口同上(接口继承tk.mybatis.mapper.common.Mapper或自定义组合);
  3. 在启动类或配置类上使用tk.mybatis.spring.annotation.MapperScan扫描 Mapper 接口(注意使用 tk 包下的注解,而非 MyBatis 官方注解)。

需要说明的是,仓库中spring-boot-starter模块 README(spring-boot-starter/README.md)展示的是较早期版本(1.2.x)的集成文档,其核心接入方式(starter 依赖 +mapper.前缀配置 + tk 包MapperScan)在当前版本中依然成立;但版本配套请以本文前述的"版本匹配说明"为准。

高级特性

主键策略:@KeySql

@KeySql 用于替换 JPA 中复杂的主键生成用法,支持四种策略(按优先级从高到低):

  • useGeneratedKeys():使用 JDBC 方式获取主键,优先级最高,设为 true 后不再校验其他配置;
  • dialect():按配置的数据库方言取回主键(IdentityDialect),优先级第二;
  • sql()/genSql():自定义取主键 SQL,或通过GenSql在初始化时生成 SQL;
  • genId():纯 Java 方式生成主键(GenId实现类),可与发号器一类服务配合。

order()默认使用全局配置中的ORDER(BEFORE/AFTER)。仓库测试 base/genid 提供了SimpleGenId、UUIdGenId等实现示例。

逻辑删除:@LogicDelete

@LogicDelete 支持字段级逻辑删除,属性包括:

  • isDeletedValue():已删除标识,默认 1;
  • notDeletedValue():未删除标识,默认 0;
  • isNullForDeletedValue():以 null 作为已删除标识(优先级高于isDeletedValue);
  • isNullForNotDeletedValue():以 null 作为未删除标识(优先级高于notDeletedValue)。

配合全局配置mapper.logicDeleteByKey=true(默认),通过主键的 select/update/delete 会自动拼接逻辑删除条件。

安全删除与安全更新

开启mapper.safeDelete=true后,框架禁止无条件全表删除。仓库测试 SafeDeleteByFieldTest 验证了四种危险场景都会抛出PersistenceException:

  • delete(new Country())(空条件对象);
  • delete(null);
  • deleteByExample(new Example(Country.class))(空 Example);
  • deleteByExample(null)。

同理mapper.safeUpdate=true保护全表更新,对应测试见 SafeUpdateByFieldTest。

Example 条件查询

Example 是通用 Mapper 内置的条件查询对象,支持orderBy、selectProperties/excludeProperties(指定/排除查询列)、and()/or()分组条件、distinct、forUpdate等。其构造函数Example(Class<?> entityClass)默认exists=true;也可使用Example.builder(entityClass)链式构建,配合 Sqls 工具类生成orderBy、select等子句。配合 Weekend 模块,还可以用 Lambda 方式(WeekendSqls)书写类型安全的条件,避免字符串属性名拼写错误。

扩展方法(extra 模块)

extra 模块提供更多实用通用方法(extra/README.md 有介绍):

  • 批量插入:tk.mybatis.mapper.additional.insert.InsertListMapper,SQL 形如insert table(xxx) values (xxx), (xxx)...,适用于 MySQL、H2 等支持多值插入的数据库;Oracle 特殊批量插入使用INSERT ALL ... SELECT 1 FROM DUAL语法(见additional.dialect.oracle.InsertListMapper),由于语法限制暂不支持序列;
  • 空字段强制更新:UpdateByPrimaryKeySelectiveForceMapper,解决UpdateByPrimaryKeySelective场景下空值也需要被设置的需求;
  • 按属性查询/删除:SelectByPropertyMapper、DeleteByPropertyMapper;
  • 按 Id 列表操作:SelectByIdListMapper、DeleteByIdListMapper;
  • 指定列查询:SelectSpecifyColumnsMapper;
  • 批量更新 / 差异更新 / Upsert:BatchUpdateMapper、UpdateByDifferMapper、UpsertMapper等;
  • 聚合查询:AggregationMapper配合AggregateCondition实现分组聚合。

设计你自己的通用方法

仓库 README 特别强调:不要局限于已有通用方法,根据自己需要选择和设计的通用方法才更满足自己的需要。结合源码,自定义通用方法的标准路径是:

  1. 定义细粒度接口并标注@RegisterMapper,方法上绑定自定义 Provider,例如:
@RegisterMapper public interface MySelectMapper<T> { @SelectProvider(type = MyProvider.class, method = "dynamicSQL") T mySelect(T record); }
  1. 继承 MapperTemplate 编写 Provider,利用EntityHelper获取实体元数据,用createSqlSource(ms, xmlSql)生成动态 SQL,其中模板方法签名需与接口方法一一对应(MapperHelper.fromMapperClasses会通过templateClass.getMethod(method.getName(), MappedStatement.class)校验,缺失会抛出异常)。

  2. 将自定义接口注册进Config.mappers或通过@RegisterMapper自动注册,即可像内置方法一样被 MapperHelper 拦截并替换 SQL。

仓库 hsqldb/HsqldbProvider 与 MultipleMapperProviderTest 都是自定义 Provider 的现成参考。

新项目建议

对于全新项目,README.md 建议关注新一代的mybatis-mapper:它完全作为 MyBatis 扩展存在,不修改 mybatis、mybatis-spring、mybatis-spring-boot-starter 任何代码,无需额外配置,可快速上手。若项目仍需停留在 Spring Boot 2.x / JDK 8 或 3.x / JDK 17,则按"版本匹配说明"选择 4.3.x 或 5.x 分支的通用 Mapper。

参考资源

  • 仓库根 README.md:版本匹配说明、Maven 坐标与更新说明;
  • core 模块:Config.java、MapperHelper.java、Example.java、注解包 annotation;
  • base 模块:通用接口 common、Provider 实现 provider,以及 base 测试集(实体、Mapper、HSQLDB 建表脚本 CreateDB.sql);
  • extra 模块:扩展通用方法及其测试;
  • spring-boot-starter 模块:自动配置与mapper.前缀属性绑定;
  • weekend 模块:Lambda 类型安全的条件查询封装。
  • 后端
  • ORM
  • 数据库

【免费下载链接】Mapper

Mybatis Common Mapper - Easy to use

项目地址:https://gitcode.com/gh_mirrors/ma/Mapper
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进