MyBatis3源码解析(4)参数解析

Mybatis入门到源码解析 MyBatis是一款优秀的基于Java的持久层框架,它内部封装了JDBC,使开发者只需要关注SQL语句本身,而不需要花费精力去处理加载驱动、创建连接、创建Statement等繁杂的过程。持久化持久化是将程序数据在持久状态和瞬时状态间转换的机制。(理解一下这个持久状态转化为瞬时状态,持久化是mybatis最重要的特性)即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中、XML数据文件中等等。 阅读详情

简介

上篇文章中探索了查询语句的执行过程,下面我们接着来看看其中的查询参数的解析细节,是如何工作的

参数的解析

在日常的开发中,常见的参数有如下几种:

  • 1.直接传入: func(Object param1, Object param2, …)
  • 2.放入Map中进行传入:func(Map<String, Object> param)
  • 3.类传入: func(Object param)

上面的请求是如何对应的呢,下面让我们带着疑问跟着源码走一走

参数解析前的相关准备工作

在上篇中,在ParamNameResolver有获取参数列表的代码,大体上从names中遍历获取的,这里就涉及到names的初始化的相关代码,如下:

public class ParamNameResolver {
    public static final String GENERIC_NAME_PREFIX = "param";
    private final boolean useActualParamName;
    private final SortedMap<Integer, String> names;
    private boolean hasParamAnnotation;

    // 初始化话的时候,将相关的name已初始化好
    public ParamNameResolver(Configuration config, Method method) {
        this.useActualParamName = config.isUseActualParamName();
        Class<?>[] paramTypes = method.getParameterTypes();
        Annotation[][] paramAnnotations = method.getParameterAnnotations();
        SortedMap<Integer, String> map = new TreeMap();
        int paramCount = paramAnnotations.length;

        for(int paramIndex = 0; paramIndex < paramCount; ++paramIndex) {
            if (!isSpecialParameter(paramTypes[paramIndex])) {
                String name = null;
                Annotation[] var9 = paramAnnotations[paramIndex];
                int var10 = var9.length;

                for(int var11 = 0; var11 < var10; ++var11) {
                    Annotation annotation = var9[var11];
                    if (annotation instanceof Param) {
                        this.hasParamAnnotation = true;
                        name = ((Param)annotation).value();
                        break;
                    }
                }

                if (name == null) {
                    if (this.useActualParamName) {
                        name = this.getActualParamName(method, paramIndex);
                    }

                    if (name == null) {
                        name = String.valueOf(map.size());
                    }
                }

                map.put(paramIndex, name);
            }
        }

        this.names = Collections.unmodifiableSortedMap(map);
    }
}

在上面的代码中,names在类构造函数中已经生成好了,后面获取值的时候直接用即可

而在ParamNameResolver的构造函数中,通过初步跟踪代码,是直接读取的接口函数参数获取得到的参数,也就是在情况3中传入类,是当做一个参数,后面这个类会一直传递下去

ParamNameResolver在MapperMethod中就已经初始化好了

public class MapperProxy<T> implements InvocationHandler, Serializable {
    private MapperProxy.MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
        try {
            return (MapperProxy.MapperMethodInvoker)MapUtil.computeIfAbsent(this.methodCache, method, (m) -> {
                if (m.isDefault()) {
		     ......
                } else {
                    return new MapperProxy.PlainMethodInvoker(new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration()));
                }
            });
        } catch (RuntimeException var4) {
            Throwable cause = var4.getCause();
            throw (Throwable)(cause == null ? var4 : cause);
        }
    }
}

public class MapperMethod {
    public static class MethodSignature {
	......
        public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
	    ......
            this.paramNameResolver = new ParamNameResolver(configuration, method);
        }
    }
}

跟踪代码下来,首先进行相关的初始化工作,而后在进行参数的解析获取

参数值初步获取

初始化完成后,在代码语句执行前,会获取参数值列表,下面是具体的处理逻辑:

public class ParamNameResolver {
    public Object getNamedParams(Object[] args) {
	// 获取参数的数量
        int paramCount = this.names.size();
        if (args != null && paramCount != 0) {
	    // 没有参数声明并且参数数量为1
            if (!this.hasParamAnnotation && paramCount == 1) {
                Object value = args[(Integer)this.names.firstKey()];
                return wrapToMapIfCollection(value, this.useActualParamName ? (String)this.names.get(0) : null);
            } else {
                Map<String, Object> param = new ParamMap();
                int i = 0;

                // 遍历name获得参数列表
                for(Iterator var5 = this.names.entrySet().iterator(); var5.hasNext(); ++i) {
                    Entry<Integer, String> entry = (Entry)var5.next();
                    param.put(entry.getValue(), args[(Integer)entry.getKey()]);
                    String genericParamName = "param" + (i + 1);
                    if (!this.names.containsValue(genericParamName)) {
                        param.put(genericParamName, args[(Integer)entry.getKey()]);
                    }
                }

                return param;
            }
        } else {
            return null;
        }
    }
}

在上面处理中,如果参数列表中唯一只有一个类参数,那这个参数也算是一个参数,会直接返回类,比如在示例中会直接返回:Person(id=1,name=1),后面获取参数值填充时,会使用类get方法获取值,这个在下面会接着分析

而且注意在param中,会存入两个东西,一个argx,一个是paramx,感觉是和${}和#{}有关,这个后面再分析,param在示例中会如下:

#如果不加@Param注解
param:
- arg0: 1
- arg1: 1
- param0: 1
- param1: 1

#如果加@Param注解
param:
- id: 1
- name: 1
- param0: 1
- param1: 1

在这里就得到后面要用的参数了,这里需要注意了,如果是单个参数,那就是直接返回对应的值;如果是多个参数,那就会放到一个map中,这个map中的key是非常关键的,因为构造preStatement是根据名称从里面取值的,后面会有相关代码

PreStatement的生成

咋上面得到参数后,并不是直接使用,而在在PreStatement生成的时候用于传入的,关键的代码如下:

public class SimpleExecutor extends BaseExecutor {
    private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Connection connection = this.getConnection(statementLog);
        Statement stmt = handler.prepare(connection, this.transaction.getTimeout());
	// 这里会进行参数的传入
        handler.parameterize(stmt);
        return stmt;
    }
}

public class DefaultParameterHandler implements ParameterHandler {
    public void setParameters(PreparedStatement ps) {
        ErrorContext.instance().activity("setting parameters").object(this.mappedStatement.getParameterMap().getId());
        List<ParameterMapping> parameterMappings = this.boundSql.getParameterMappings();
        if (parameterMappings != null) {
            for(int i = 0; i < parameterMappings.size(); ++i) {
                ParameterMapping parameterMapping = (ParameterMapping)parameterMappings.get(i);
                if (parameterMapping.getMode() != ParameterMode.OUT) {
                    String propertyName = parameterMapping.getProperty();
                    Object value;
                    if (this.boundSql.hasAdditionalParameter(propertyName)) {
                        value = this.boundSql.getAdditionalParameter(propertyName);
                    } else if (this.parameterObject == null) {
                        value = null;
                    } else if (this.typeHandlerRegistry.hasTypeHandler(this.parameterObject.getClass())) {
                        value = this.parameterObject;
                    } else {
			// 上面的三个先不管,下面就是获取参数的具体的逻辑
			// 如果是类,会通过一些处理调用对应的get方法
			// 如果多个之间传递的参数,在上面会放入一个map,之间从map中获取即可
                        MetaObject metaObject = this.configuration.newMetaObject(this.parameterObject);
                        value = metaObject.getValue(propertyName);
                    }

                    // 和参数拦截器相关的,后面再解析,先放过
                    TypeHandler typeHandler = parameterMapping.getTypeHandler();
                    JdbcType jdbcType = parameterMapping.getJdbcType();
                    if (value == null && jdbcType == null) {
                        jdbcType = this.configuration.getJdbcTypeForNull();
                    }

                    try {
			// 设置相关的值
                        typeHandler.setParameter(ps, i + 1, value, jdbcType);
                    } catch (SQLException | TypeException var10) {
                        throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + var10, var10);
                    }
                }
            }
        }

    }
}

从上面大致代码可以看到,在正常情况下,参数的设置都是通过名称取获取的,之间传入或者单个传入的情况比较简单

  • 多个参数的情况下,会将所有的参数放入map中,后面根据名称去获取
  • 单个参数类的情况下,会调用类的get方法对应获取

那如果混合类型,比如下面的情况:

public interface PersonMapper {
    @Select("Select id, name from Person where id=#{id} and name=#{person.name}")
    @Results(value = {
            @Result(property = "id", column = "id"),
            @Result(property="name", column = "name"),
    })
    Person getPersonByMul(@Param("person") Person person, @Param("id") Integer id);
}

我们一直根据下,从下面的代码中得到,如果是类,会递归去获取

public class MetaObject {
    public Object getValue(String name) {
        PropertyTokenizer prop = new PropertyTokenizer(name);
        if (prop.hasNext()) {
            MetaObject metaValue = this.metaObjectForProperty(prop.getIndexedName());
	    // 如果是类,后面会再次调用getVale获取
            return metaValue == SystemMetaObject.NULL_META_OBJECT ? null : metaValue.getValue(prop.getChildren());
        } else {
            return this.objectWrapper.get(prop);
        }
    }
}

总结

通过本篇的探索,我们大致了解了MyBatis3的参数获取解析原理

  • 1.在初始化阶段,获取接口函数的参数列表,初始化names,用于后面获取参数值
  • 2.获取参数值,分单个或者多个参数的情况,而且根据参数的类型,有不同的处理方法
    • 单个参数:直接返回
    • 多个参数:放入Map中返回,后面根据key进行获取
    • 参数类型是类:后面调用类的get防护获取对应的值
    • 参数类型是Map:后面直接调用get方法
  • 3.PreStatement生成,生成的时候对应的值从上面得到的参数值获取
深入解析MyBatis源码与实战 摘要:本文系统解析MyBatis核心架构与实现原理,涵盖配置解析、SQL执行、结果映射三大模块。通过动态SQL生成、缓存机制(一级/二级缓存)、插件开发(Interceptor接口)等典型示例,深入剖析源码实现。结合SpringBoot集成场景,展示CRUD操作、动态SQL拼接、事务管理、多数据源配置等实战方案,并给出批量操作优化(BatchExecutor)、类型处理器扩展等高级特性实现。特别说明流程图关键节点:从SqlSessionFactory构建到Executor执行,通过StatementHand 阅读详情

相关推荐

mybatis3.x源码深度解析与最佳实践.pdf

mybatis3.x源码深度解析与最佳实践.pdf

MyBatis-@param注解详解

@param参数注解 一、@Param注解单一属性 dao层示例 Public User selectUser(@param(“userName”) String name,@param(“userpassword”) String password); xml映射对应示例 &amp;amp;amp;amp;amp;amp;amp;lt;select id=&amp;amp;amp;amp;amp;amp;quot; selectUser&amp;amp;amp;amp;amp;a

xuonline4196的博客 2万+

从TCGA官网获取新版count数据并精准转换为TPM的实战指南

本文详细介绍了从TCGA官网获取新版count数据并精准转换为TPM的实战指南。通过TCGAbiolinks工具下载原始count数据,结合GENCODE v36注释文件计算基因有效长度,最终实现count到TPM的标准化转换,确保数据完整性和分析可靠性。文章包含完整代码示例和常见问题解决方案,特别适合生物信息学研究人员参考。

weixin_27310417的博客 283

29、ParamNameResolver参数解析

处理接口形式的入参在使用MyBatis时,有两种使用方法。 - 一种是使用的接口形式, - 另一种是通过SqlSession调用命名空间。这两种方式在传递参数时是不一样的,命名空间的方式更直接ParamNameResolver主要用来处理接口形式的参数,最后会把参数处放在一个map中map的key为参数的位置,value为参数的名字字段 private static final String

等风的帆 1949

2023年数学建模国赛三大赛题核心建模思路与实战代码全解

本文全面解析2023年数学建模国赛三大赛题的核心建模思路与实战代码,涵盖塔式太阳能光热发电系统优化、多波束测深系统动态规划及蔬菜商品化策略建模。通过Python代码示例和优化算法(如遗传算法、NSGA2)展示,帮助参赛者掌握数学建模的关键技术与实战技巧,提升竞赛成绩。

weixin_33713350的博客 331

5G NR 双连接与载波聚合:PCell、SCell、PScell、SpCell 角色全解析

本文全面解析了5G NR中双连接与载波聚合技术的关键概念,详细介绍了PCell、SCell、PScell和SpCell的角色与功能。通过实际案例和配置示例,帮助读者理解这些小区在提升网络覆盖和速率方面的作用,以及在实际组网中的优化策略。

weixin_33220360的博客 373

5.10 mybatis之useActualParamName作用

useActualParamName官方解释:允许使用方法签名中的名称作为语句参数名称。为了使用该特性,你的项目必须采用 Java 8编译,并且加上 -parameters 选项。(新增于3.4.1)。useActualParamName有2个值,true和false,如不配置默认为true。如何理解这句话呢?

u010502101的博客 1125

CAN总线BusOff恢复机制详解:快慢恢复策略在车载网络中的实战应用

本文深入解析了CAN总线BusOff恢复机制,重点阐述了快慢恢复策略在车载网络中的工程应用。通过对比NXP S32K与STM32平台的实现代码,并结合CANoe仿真与参数优化方法,为工程师提供了从理论到实战的完整解决方案,旨在提升车载网络的可靠性与功能安全。

7up55的博客 613

MyBatisSQL参数占位符与全局属性useActualParamName详细介绍与原理剖析

MyBatisSQL参数占位符与全局属性useActualParamName详细介绍与原理剖析

铭记: 3157

复合材料热压罐固化残余应力仿真与优化

复合材料在热压罐固化过程中产生的残余应力是影响构件性能的关键因素,尤其在航空航天和风电叶片等高端制造领域。通过多物理场耦合仿真技术,可以精确预测残余应力分布,优化工艺参数。顺序耦合方法分步求解热分析和结构分析,既保证了计算精度,又控制了成本。Abaqus等有限元工具在此过程中发挥重要作用,通过合理设置边界条件、材料属性和接触关系,能够有效模拟温度场和应力场的相互作用。工程实践中,这种仿真方法已成功应用于无人机机翼、卫星支架等实际项目,显著降低了固化变形和微裂纹风险。

weixin_33146151的博客 212

Abaqus6.14.4 Linux详细图文安装教程

Abaqus6.14.4 Linux详细图文安装教程 20170409 1. 准备安装环境 1.1 安装CentOS 6.5,更新库 1.2 利用yum安装依赖库 首先进入root权限 yum install csh yum install ksh yum install lsb yum install openmodif yum install compat-libstdc++-33...

snuhxm的博客 7841

【Mybatis源码学习】参数解析

【Mybatis源码学习】参数解析一、几种入参形式1.单个入参2.多个入参3.入参为实体对象4.入参为Map二、mybatis执行入口三、参数解析流程四、总结 一、几种入参形式 这里只分析带有入参的方法。 1.单个入参 2.多个入参 3.入参为实体对象 4.入参为Map 二、mybatis执行入口 三、参数解析流程 四、总结 ...

kezade的blog 456

kettle9.0+ 连接Hadoop报错

kettle bug

x²+(y-√³x²)²=1的博客 1559

Mybatis3 源码解析系列

Mybatis作为一个优秀的Java持久化框架,在我们的日常工作中相信都会用到,本次源码解析系列,就开始探索下Mybatis

github_35735591的博客 1076

SpringBoot 整合Jasypt 实现配置文件加密读取操作详解

SpringBoot 整合Jasypt 实现配置文件加密读取操作详解

congge 4099

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

mybatis的mapper方法参数解析过程源码

文章目录 前言 一、参数解析过程源码 二、BeanWrapper和MapWrapper的get方法 1.BeanWrapper的get方法 2.MapWrapper的get方法 三、@Param对应的ParamMap的生成过程 总结 前言 Mybatis的mapper方法传递参数可以通过@param,对象object,map和list等方式。本文从源码层次分析每种参数传递的流程。 一、参数解析过程源码 参数解析的过程比较复杂,本文就重点代码进行分析。 public void...

qq_35219329的博客 1155

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

Spring源码解析(十):spring整合mybatis源码

通过sqlSession获取对应的代理对象代理对象执行sql完成数据库操作// 1.解析XML配置// 2.基于解析好的XML配置创建一个SqlSessionFactory// 3.通过SqlSessionFactory,创建一个SqlSession// 4.获取一个代理对象// 5.调用代理对象的方法System.out.println("代理对象查询结果:" + mapper.selectOne(1));根据@MapperScan注解扫描指定路径下接口。

从基础到源码解析的学习记录 1273

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

MyBatis3源码深度解析(一)搭建MyBatis源码环境

笔者作为一名Java开发人员,工作中的项目几乎都将MyBatis作为持久层框架,但笔者对MyBatis框架的理解仅局限于使用,并不理解框架底层的实现原理。为此,为提升自身编码能力,笔者决定深入学习MyBatis的底层源码,并在优快云发表学习笔记。学习参考书是:MyBatis 3源码深度解析 江荣波 清华大学出版社2002年,iBatis框架发布,引入SQL映射;2010年,iBatis框架改名为MyBatis,迁移到Google Code;2013年,MyBatis迁移到Github。

挑灯日记 记录自己 1564

Mybatis源码解析

Mybatis是支持定制化SQL、存储过程和高级映射的持久层框架。主要完成两件事: 封装JDBC的操作 利用反射完成Java类和SQL之间的转换 mybatis的主要目的就是管理执行SQL是参数的输入和输出,编写SQL和结果集的映射是mybatis的主要优点 mybatis中主要类和接口 Configuration:将mybatis配置文件中的信息保存到该类中 SqlSessionFac...

一点点的积累 7111

mybatis传入和返回参数解析

mybatis传入和返回参数解析

架构师:通透,才能写出好代码! 1659

《MyBatis3源码深度解析》图书简介

一、图书封面 二、书籍目录 前言 4 第1篇 Mybatis3源码 7 第1章 搭建Mybatis源码环境 7 1.1 Mybatis3简介 7 1.2 环境准备 7 1.3 获取Mybatis源码 8 1.4 导入Mybatis源码到IDE 10 1.5 HSQLDB数据库简介 13 1.6 本章小结 16 第2章 JDBC规范详解 17 2.1 JDBC API简介 17 2.1.1 建立数...

Coding... 4620

mybatis源码解析-xml文件的解析

mybatis源码解析xml文件的流程

m0_54258715的博客 1285

MyBatis——源码解析MyBatis框架底层的执行原理

mybatis底层执行原理(源码解析)

贝尔摩德苦艾酒 7501

【mybatis】Mybatis整体架构解析

【mybatis】Mybatis整体架构解析

芝兰生于深谷,不以无人而不芳 2000
上一篇: MyBatis3源码解析(3)查询语句执行
下一篇: MyBatis3源码解析(5)查询结果处理
_萧_
博客等级 码龄10年 55粉丝 157原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值