当前位置: 首页 > news >正文

揭秘MyBatis核心类MappedStatement

MappedStatement-封装一次sql映射语句的所有配置信息

  • MappedStatement: 是 MyBatis 框架中核心类

核心功能:封装一次 SQL 映射语句(SQL Statement)的所有配置信息

​ 使用了内部静态类 Builder 来实现构建者模式, 保证 MappedStatement 的不可变性和构造过程的灵活性

作用
MappedStatement : 代表 MyBatis 中一个具体的映射语句配置,包含了执行 SQL 所需的所有信息

MappedStatement.Builder 是用于构建 MappedStatement 实例的构造器,避免构造参数过多,代码易读且易扩展。

public static class Builder {private MappedStatement mappedStatement = new MappedStatement();public Builder(Configuration configuration, String id, SqlSource sqlSource, SqlCommandType sqlCommandType) {// 必填参数初始化及默认值赋值}// 各种 setter 风格的方法链,方便链式调用public Builder resource(String resource) { ... }public Builder fetchSize(Integer fetchSize) { ... }// ... 省略public MappedStatement build() {// 校验必须字段非空// 包装不可变的 List 等return mappedStatement;}
}

主要字段说明

字段名类型作用描述
resourceString映射语句来源资源(一般为 XML 路径)
configurationConfigurationMyBatis 全局配置对象
idString映射语句唯一标识(namespace + id)
fetchSizeInteger批量获取结果集大小
timeoutInteger执行超时时间
statementTypeStatementTypeSQL 语句类型(STATEMENT, PREPARED, CALLABLE)
resultSetTypeResultSetType结果集类型(FORWARD_ONLY 等)
sqlSourceSqlSourceSQL 语句提供者,负责动态 SQL 解析
cacheCache缓存策略
parameterMapParameterMap参数映射
resultMapsList<ResultMap>结果映射列表
flushCacheRequiredboolean是否需要刷新缓存
useCacheboolean是否使用二级缓存
resultOrderedboolean结果是否有序
sqlCommandTypeSqlCommandTypeSQL 类型(SELECT, INSERT, UPDATE, DELETE)
keyGeneratorKeyGenerator主键生成器
keyPropertiesString[]主键属性列表
keyColumnsString[]主键列列表
hasNestedResultMapsboolean是否存在嵌套的结果映射
databaseIdString数据库标识,支持多数据库映射
statementLogLog语句执行日志
langLanguageDriver语言驱动,处理不同脚本语言(XML、注解等)
resultSetsString[]多结果集名称

Builder 中常用方法详解

  • parameterMap(ParameterMap parameterMap):指定参数映射。
  • resultMaps(List<ResultMap> resultMaps):指定结果映射,并根据结果映射判断是否有嵌套结果映射。
  • keyGenerator(KeyGenerator keyGenerator):指定主键生成策略,默认根据配置和 SQL 类型确定。
  • keyProperty(String keyProperty) / keyColumn(String keyColumn):指定主键对应的属性名和列名。
  • lang(LanguageDriver driver):指定语言驱动,如 XML 语言驱动。
  • build():构造最终的不可变 MappedStatement 实例。

关键方法

  • 根据参数对象,从 sqlSource 解析出最终 SQL 和参数映射。
  • 如果没有参数映射,则使用默认参数映射重构。
  • 进一步检查参数映射中是否有嵌套结果映射,标记 hasNestedResultMaps
public BoundSql getBoundSql(Object parameterObject) {BoundSql boundSql = sqlSource.getBoundSql(parameterObject);List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();if (parameterMappings == null || parameterMappings.isEmpty()) {boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);}// 检查参数映射中是否包含嵌套结果映射(解决 issue #30)for (ParameterMapping pm : boundSql.getParameterMappings()) {String rmId = pm.getResultMapId();if (rmId != null) {ResultMap rm = configuration.getResultMap(rmId);if (rm != null) {hasNestedResultMaps |= rm.hasNestedResultMaps();}}}return boundSql;
}

此方法不能用的原因

在用 MyBatis-Plus + MybatisInterceptor 链机制,而你注册的是 原始 MyBatis 插件机制

MyBatis-Plus 3.4.0+ 默认只支持 InnerInterceptor

注册的是 MyBatis 原生的插件(实现了 org.apache.ibatis.plugin.Interceptor),
而 MyBatis-Plus 的默认插件执行链不包含原生 Interceptor,必须你通过手动注册。

你可以从日志中看到这些被拦截的类是:

复制编辑
plugin target: class com.baomidou.mybatisplus.core.MybatisParameterHandler

这是 MyBatis-Plus 定制过的执行器链条(它使用了自己的 InterceptorChain)。

故整体变动改为实现 InnerInterceptor

@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds,ResultHandler resultHandler, BoundSql boundSql) {// 判断是否跳过if (MerchantIsolationContext.isSkipped()) {return;}try {String originalSql = boundSql.getSql().toLowerCase(Locale.ROOT);log.info("===> into MerchantIsolationInnerInterceptor, msId = {}, SQL = {}", ms.getId(), originalSql);if (!originalSql.contains("from") || originalSql.contains(MerchantCenterConstant.FIELD_MERCHANT_ID)) {return;}Class<?> doClass = extractDoClass(ms);if (CheckUtil.isEmpty(doClass)) {log.warn("Unable to extract DO class, skipping merchant_id injection");return;}if (!hasMerchantId(doClass)) {return;}if (isMultiTable(originalSql)) {log.warn("Multi-table SQL detected, skipping merchant_id injection: {}", originalSql);return;}Long merchantId = extractMerchantId(parameter);if (CheckUtil.isEmpty(merchantId)) {throw BaseIllegalArgumentException.newException(BaseIllegalArgumentException.BASE_ILLEGAL_ARGUMENT,"No merchant_id provided and no skip claim");}String modifiedSql = appendMerchantCondition(boundSql.getSql(), merchantId);log.info("SQL injection merchant_id completed: {}", modifiedSql);// 替换 SQL(MyBatis-Plus 特殊处理)ReflectUtil.setFieldValue(boundSql, "sql", modifiedSql);} catch (Exception e) {log.error("MerchantIsolationInnerInterceptor Error: {}", e.getMessage(), e);}
}
http://www.lryc.cn/news/615214.html

相关文章:

  • 多模态RAG赛题实战--Datawhale AI夏令营
  • 如何分析需求的可行性
  • 生产环境某业务服务JVM调优总结
  • 避免在微信小程序中频繁使用setData方法
  • 扩散LLM推理新范式:打破生成长度限制,实现动态自适应调节
  • 机器学习——09 聚类算法
  • BGP 协议笔记
  • 使用qemu运行与GDB调试内核
  • 微软推出革命性AI安全工具Project IRE,重塑网络安全防御新范式
  • 用天气预测理解分类算法-从出门看天气到逻辑回归
  • Kubernetes(K8s)不同行业的典型应用场景及价值分析 原创
  • windows、linux应急响应入侵排查
  • Qdrant Filtering:must / should / must_not 全解析(含 Python 实操)
  • 【2025】Datawhale AI夏令营-多模态RAG-Task1、Task2笔记-任务理解与Baseline代码解读
  • 金融通用智能体(Financial General Agent, FGA)的端到端解决方案
  • 机器翻译中的语言学基础详解(包括包括语法、句法和语义学等)
  • C语言:构造类型
  • TDengine IDMP 产品基本概念
  • 使用 Visual Studio 2022 编译 PortAudio 项目
  • occworld(1):论文解读
  • Ghost备份分区设置分包大小方法
  • 任务发布悬赏查询管理地推抖音快手微信任务赚佣金网站源码功能详解二开
  • 谷歌警告云存储桶劫持攻击
  • 让大模型 “睡觉”:把版本迭代当作人类睡眠来设计(附可直接改造的训练作息表与代码)
  • n沟道增强型mos管
  • B.10.01.6-DDD领域驱动设计:从理论到落地的完整指南
  • Typora上传图片保存到assets目录下
  • 第十四届蓝桥杯青少年组省赛 编程题真题题解
  • stm32项目(24)——基于STM32的汽车CAN通信系统
  • WinForm 复合控件(用户控件):创建与使用指南