【源码解读之 Mybatis】【核心篇】–第7篇:ParameterHandler参数处理机制

第7篇:ParameterHandler参数处理机制

1. 学习目标确认

1.0 第6篇思考题解答

在深入学习ParameterHandler参数处理机制之前,让我们先回顾并解答第6篇中提出的思考题,这将帮助我们更好地理解ParameterHandler在整个执行流程中的关键作用。

思考题1:为什么MyBatis要设计RoutingStatementHandler?直接使用具体的StatementHandler不行吗?

答案要点

  • 统一入口:RoutingStatementHandler提供统一的创建入口,屏蔽了具体实现的复杂性
  • 策略模式:根据StatementType动态选择合适的处理器,实现了策略模式
  • 扩展性:便于添加新的StatementHandler类型,不影响现有代码
  • 简化调用:Executor只需要与RoutingStatementHandler交互,不需要关心具体类型
  • 插件支持:为插件拦截提供统一的拦截点

与ParameterHandler的关系:每种StatementHandler都需要ParameterHandler来设置SQL参数,RoutingStatementHandler确保了参数处理的一致性。

思考题2:PreparedStatementHandler相比SimpleStatementHandler有什么优势?为什么是默认选择?

答案要点

  • 安全优势:支持参数绑定,有效防止SQL注入攻击
  • 性能优势:SQL预编译,相同SQL结构可以复用执行计划,减少10-20%的SQL解析时间(视数据库实现)
  • 类型安全:通过TypeHandler进行类型转换,确保类型安全
  • 维护性:参数与SQL分离,代码结构更清晰
  • 功能丰富:支持主键生成、批量操作等高级功能

ParameterHandler的重要性:PreparedStatementHandler的核心优势来自于ParameterHandler的参数绑定机制。

思考题3:CallableStatementHandler如何处理存储过程的输出参数?与普通查询有什么区别?

答案要点

  • 参数注册:需要预先注册输出参数的类型和位置
  • 参数模式:支持IN、OUT、INOUT三种参数模式
  • 结果获取:通过CallableStatement.getXxx()方法获取输出参数值
  • 处理时机:在SQL执行完成后处理输出参数
  • 类型转换:输出参数也需要进行类型转换
  • 多结果集支持:复杂存储过程可能返回多个结果集,需通过ResultSetHandler.handleMultipleResults()方法处理

存储过程多结果集处理示例

// 调用存储过程,处理多个结果集和输出参数 CallableStatement cs = connection.prepareCall("{call getUserOrders(?, ?)}");  // 注册输出参数 cs.setLong(1, userId);  // 输入参数:用户ID cs.registerOutParameter(2, Types.INTEGER);  // 输出参数:订单总数  // 执行存储过程 cs.execute();  // 处理第一个结果集(用户信息) ResultSet rs1 = cs.getResultSet(); while (rs1.next()) {     System.out.println("用户: " + rs1.getString("name")); }  // 切换到下一个结果集(订单列表) if (cs.getMoreResults()) {     ResultSet rs2 = cs.getResultSet();     while (rs2.next()) {         System.out.println("订单: " + rs2.getString("order_no"));     } }  // 获取输出参数 int totalOrders = cs.getInt(2); System.out.println("订单总数: " + totalOrders); 

ParameterHandler的扩展:CallableStatementHandler需要扩展ParameterHandler来处理输出参数的注册和获取。

思考题4:StatementHandler与ParameterHandler、ResultSetHandler是如何协作的?

答案要点

  • 组合模式:StatementHandler通过组合包含ParameterHandler和ResultSetHandler
  • 职责分工:StatementHandler负责Statement管理,ParameterHandler负责参数设置,ResultSetHandler负责结果处理
  • 执行流程:prepare() → parameterize() → execute() → handleResults()
  • 统一管理:StatementHandler统一协调三者的执行时序

核心协作流程

StatementHandler.prepare()     // 创建Statement ↓ ParameterHandler.setParameters() // 设置参数 ↓ Statement.execute()            // 执行SQL ↓ ResultSetHandler.handleResultSets() // 处理结果 

1.1 本篇学习目标

通过本文你将能够:

  1. 深入理解ParameterHandler参数处理器的设计思想和核心职责
  2. 掌握DefaultParameterHandler的实现机制和参数设置流程
  3. 理解参数映射(ParameterMapping)的配置和使用方式
  4. 掌握TypeHandler类型处理器与参数处理的协作关系
  5. 了解不同参数类型(基本类型、对象、集合)的处理策略
  6. 理解额外参数(AdditionalParameter)的生成与使用机制
  7. 掌握参数验证和性能优化策略
  8. 具备自定义ParameterHandler扩展开发的能力

2. ParameterHandler参数处理器体系总览

还记得第6篇我们说StatementHandler是"SQL执行的总指挥"吗?那ParameterHandler就是这位总指挥手下最得力的"后勤部长"——专门负责把我们Java代码里的参数"翻译"成数据库能听懂的话。

想象一下,你要给外国朋友寄包裹,得先把中文地址翻译成英文对吧?ParameterHandler做的就是这个活儿:把 User user = new User("张三", 18)这样的Java对象,翻译成 ps.setString(1, "张三"); ps.setInt(2, 18)这样的JDBC调用。比如将 #{user.name}转换为 ps.setString(1, "张三")这样神奇的操作!

2.1 参数处理器继承关系图

classDiagram class ParameterHandler { <<interface>> +getParameterObject() Object +setParameters(PreparedStatement) void } class DefaultParameterHandler { -typeHandlerRegistry TypeHandlerRegistry -mappedStatement MappedStatement -parameterObject Object -boundSql BoundSql -configuration Configuration +DefaultParameterHandler(MappedStatement, Object, BoundSql) +getParameterObject() Object +setParameters(PreparedStatement) void } class ParameterMapping { -property String -mode ParameterMode -javaType Class -jdbcType JdbcType -typeHandler TypeHandler -numericScale Integer -resultMapId String -jdbcTypeName String -expression String } class TypeHandler { <<interface>> +setParameter(PreparedStatement, int, T, JdbcType) void +getResult(ResultSet, String) T +getResult(ResultSet, int) T +getResult(CallableStatement, int) T } class LanguageDriver { <<interface>> +createParameterHandler(MappedStatement, Object, BoundSql) ParameterHandler } class XMLLanguageDriver { +createParameterHandler(MappedStatement, Object, BoundSql) ParameterHandler } ParameterHandler <|.. DefaultParameterHandler LanguageDriver <|.. XMLLanguageDriver DefaultParameterHandler --> ParameterMapping : uses DefaultParameterHandler --> TypeHandler : delegates to XMLLanguageDriver --> DefaultParameterHandler : creates

2.2 参数处理器职责分工

组件 核心职责 主要功能 性能特点
ParameterHandler 参数处理接口 定义参数设置规范 统一入口,零性能损耗
DefaultParameterHandler 默认参数处理实现 参数值获取、类型转换、参数设置 内置优先级策略,高效稳定
ParameterMapping 参数映射配置 参数属性、类型、模式配置 建造者模式,减少对象创建开销
TypeHandler 类型转换处理 Java类型与JDBC类型互转 TypeHandler缓存可提升5-10%参数设置效率
LanguageDriver 语言驱动器 创建ParameterHandler实例 动态选择实现,支持多种SQL方言

2.3 参数处理流程图

sequenceDiagram participant SH as StatementHandler participant PH as ParameterHandler participant PM as ParameterMapping participant TH as TypeHandler participant PS as PreparedStatement SH->>PH: setParameters(ps) Note over PH: MetaObject缓存反射结果<br/>优化复杂对象处理 loop 遍历参数映射 PH->>PM: getProperty() PH->>PH: getPropertyValue() alt 额外参数 PH->>PH: boundSql.getAdditionalParameter() else 基本类型 PH->>PH: parameterObject直接使用 else 复杂对象 Note over PH: 使用MetaObject反射<br/>性能开销较大 PH->>PH: metaObject.getValue() end PH->>TH: setParameter(ps, index, value, jdbcType) TH->>PS: setXxx(index, value) end PS-->>SH: 参数设置完成

3. ParameterHandler接口定义

3.1 接口源码分析

ParameterHandler接口非常简洁,只定义了两个核心方法:

package org.apache.ibatis.executor.parameter;  import java.sql.PreparedStatement; import java.sql.SQLException;  /**  * 参数处理器接口  * 负责为PreparedStatement设置参数  *   * @author Clinton Begin  */ public interface ParameterHandler {      /**      * 获取参数对象      * @return 参数对象,可能为null、基本类型(如Long、String)、复杂对象(如User)或集合(如List<User>)      *       * 示例返回值:      * - 基本类型:Long id = 123L      * - 复杂对象:User user = new User()      * - 集合类型:List<Long> ids = Arrays.asList(1L, 2L, 3L)      * - 空值:null(无参数方法)      */     Object getParameterObject();        // 使用示例     // Object param = handler.getParameterObject();     // 可能是 Long、User、List<User> 或 null      /**      * 为PreparedStatement设置参数      * 这是ParameterHandler的核心方法,负责:      * 1. 获取参数值      * 2. 进行类型转换      * 3. 调用PreparedStatement的setXxx方法设置参数      *       * @param ps PreparedStatement对象      * @throws SQLException 参数设置过程中的SQL异常      */     void setParameters(PreparedStatement ps) throws SQLException; } 

3.2 接口设计特点

简洁得让人惊讶

  • 就两个方法!getParameterObject()和setParameters(),简单到你都怀疑"这就完了?"
  • 但别小看它,MyBatis的"单一职责原则"在这儿体现得淋漓尽致

扩展性拉满

  • 想加个参数加密?写个实现类就行
  • 想做参数校验?也是写个实现类
  • 这就是面向接口编程的魅力啊!

团队协作能手

  • 跟TypeHandler配合:"兄弟,这个Date类型你来转一下"
  • 跟StatementHandler配合:"老大,参数我都设置好了,可以执行了!"

4. DefaultParameterHandler默认实现

4.1 核心源码分析

DefaultParameterHandler是ParameterHandler的默认实现,承担了参数处理的核心逻辑:

package org.apache.ibatis.scripting.defaults;  import org.apache.ibatis.executor.ErrorContext; import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.ParameterMapping; import org.apache.ibatis.mapping.ParameterMode; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.TypeException; import org.apache.ibatis.type.TypeHandler; import org.apache.ibatis.type.TypeHandlerRegistry;  import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.List;  /**  * 默认参数处理器实现  * 负责将Java参数对象转换为PreparedStatement的参数  *   * @author Clinton Begin  * @author Eduardo Macarron  */ public class DefaultParameterHandler implements ParameterHandler {      // 类型处理器注册表,用于获取对应的TypeHandler     private final TypeHandlerRegistry typeHandlerRegistry;        // SQL映射语句,包含参数映射配置     private final MappedStatement mappedStatement;        // 参数对象,可能是null、基本类型、Map、POJO等     private final Object parameterObject;        // 绑定SQL对象,包含参数映射列表     private final BoundSql boundSql;        // MyBatis全局配置对象     private final Configuration configuration;      /**      * 构造方法      * 初始化参数处理器所需的核心组件      *       * @param mappedStatement SQL映射语句,包含参数映射配置      * @param parameterObject 参数对象,可能为null、基本类型、复杂对象或集合      * @param boundSql 绑定SQL对象,包含参数映射列表和额外参数      */     public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {         this.mappedStatement = mappedStatement;         this.configuration = mappedStatement.getConfiguration();         this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();         this.parameterObject = parameterObject;         this.boundSql = boundSql;     }      @Override     public Object getParameterObject() {         return parameterObject;     }      /**      * 设置PreparedStatement参数的核心方法      * 遍历所有参数映射,为每个参数设置值      *       * 核心处理流程(伪代码):      * for (ParameterMapping pm : boundSql.getParameterMappings()) {      *     Object value = getPropertyValue(pm, metaObject);  // 优先级策略获取值      *     TypeHandler th = pm.getTypeHandler();           // 获取类型处理器      *     th.setParameter(ps, index++, value, pm.getJdbcType()); // 设置参数      * }      *       * MetaObject缓存优化: MetaObject内部使用Reflector缓存属性元数据(getter/setter方法、字段类型等),      * 避免重复反射解析,显著减少复杂对象处理的反射开销      */     @Override     public void setParameters(PreparedStatement ps) throws SQLException {         // 设置错误上下文,便于问题定位         ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());                // 获取参数映射列表         List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();                if (parameterMappings != null) {             MetaObject metaObject = null;                        // 遍历每个参数映射             for (int i = 0; i < parameterMappings.size(); i++) {                 ParameterMapping parameterMapping = parameterMappings.get(i);                                // 跳过输出参数(OUT参数用于存储过程)                 if (parameterMapping.getMode() != ParameterMode.OUT) {                     Object value;                     String propertyName = parameterMapping.getProperty();                                        // 参数值获取的优先级策略                     if (boundSql.hasAdditionalParameter(propertyName)) {                         // 1. 优先从额外参数中获取(如foreach生成的参数)                         value = boundSql.getAdditionalParameter(propertyName);                     } else if (parameterObject == null) {                         // 2. 参数对象为null                         value = null;                     } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {                         // 3. 参数对象是基本类型(有对应的TypeHandler)                         value = parameterObject;                     } else {                         // 4. 参数对象是复杂类型,通过反射获取属性值                         // MetaObject使用Reflector缓存属性元数据,减少反射开销                         if (metaObject == null) {                             metaObject = configuration.newMetaObject(parameterObject);                         }                         value = metaObject.getValue(propertyName);                     }                                        // 获取对应的类型处理器                     TypeHandler typeHandler = parameterMapping.getTypeHandler();                     JdbcType jdbcType = parameterMapping.getJdbcType();                                        // 处理null值的JDBC类型                     if (value == null && jdbcType == null) {                         jdbcType = configuration.getJdbcTypeForNull();                     }                                        try {                         // 使用TypeHandler设置参数                         typeHandler.setParameter(ps, i + 1, value, jdbcType);                     } catch (TypeException | SQLException e) {                         // 封装异常信息,便于问题定位                         throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);                     }                 }             }         }     } } 

4.2 参数值获取策略

DefaultParameterHandler获取参数值可不是瞎猜的,人家有一套严格的"优先级规则",就像你找东西一样——先找最有可能的地方:

// 第一优先级:额外参数(最高优先级) if (boundSql.hasAdditionalParameter(propertyName)) {     // "哎,这个参数是foreach动态生成的,我先用这个!"     value = boundSql.getAdditionalParameter(propertyName); } else if (parameterObject == null) {     // 第二优先级:参数对象是null     // "啊?你啥参数都没给我?那我也没办法,只能是null了"     value = null; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {     // 第三优先级:基本类型     // "哦,你就传了个Long/String?那我直接用就行了"     value = parameterObject; } else {     // 第四优先级:复杂对象(最低优先级)     // "嗯?User对象?我得反射一下拿到user.getName()这些属性值"     if (metaObject == null) {         metaObject = configuration.newMetaObject(parameterObject);     }     value = metaObject.getValue(propertyName); } 

为什么要这样设计?

  1. 额外参数优先:动态SQL(像foreach)会生成临时参数,这些肯定是最新的,当然要先用
  2. null处理:空指针异常是程序员的噩梦,先判断null能避免很多问题
  3. 基本类型直接用Long id = 123L这种,还反射个啥?直接用多省事
  4. 复杂对象才反射:User、Order这些对象,没办法,只能用反射了(虽然慢点,但准确啊)

4.3 类型处理器协作

DefaultParameterHandler跟TypeHandler的配合就像是"翻译官"和"专业术语顾问"的关系:

内置TypeHandler全家桶

  • StringTypeHandler:String ↔ VARCHAR,这个最常用
  • IntegerTypeHandler:Integer ↔ INTEGER,处理数字的
  • DateTypeHandler:Date ↔ TIMESTAMP,时间类型专用
  • EnumTypeHandler:枚举 ↔ VARCHAR,枚举值的好帮手

它俩怎么配合的?

// 获取类型处理器 TypeHandler typeHandler = parameterMapping.getTypeHandler(); JdbcType jdbcType = parameterMapping.getJdbcType();  // 处理null值的JDBC类型 if (value == null && jdbcType == null) {     jdbcType = configuration.getJdbcTypeForNull(); }  try {     // 委托给TypeHandler进行具体的参数设置     // TypeHandler会根据Java类型和JDBC类型进行转换     typeHandler.setParameter(ps, i + 1, value, jdbcType); } catch (TypeException | SQLException e) {     throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e); } 

协作示例: 当参数类型为 String 时,StringTypeHandler 会调用 PreparedStatement.setString(index, value) 设置参数。

内置TypeHandler使用示例:

// StringTypeHandler 处理字符串参数 TypeHandler<String> stringHandler = new StringTypeHandler(); stringHandler.setParameter(ps, 1, "张三", JdbcType.VARCHAR);  // IntegerTypeHandler 处理整数参数 TypeHandler<Integer> intHandler = new IntegerTypeHandler(); intHandler.setParameter(ps, 2, 25, JdbcType.INTEGER);  // DateTypeHandler 处理日期参数 TypeHandler<Date> dateHandler = new DateTypeHandler(); dateHandler.setParameter(ps, 3, new Date(), JdbcType.TIMESTAMP); 

自定义TypeHandler示例

/**  * 自定义日期类型处理器  * 将Java Date转换为JDBC Timestamp  */ public class CustomDateTypeHandler implements TypeHandler<Date> {        @Override     public void setParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType) throws SQLException {         if (parameter == null) {             ps.setNull(i, Types.TIMESTAMP);         } else {             ps.setTimestamp(i, new Timestamp(parameter.getTime()));         }     }        @Override     public Date getResult(ResultSet rs, String columnName) throws SQLException {         Timestamp timestamp = rs.getTimestamp(columnName);         return timestamp != null ? new Date(timestamp.getTime()) : null;     }        @Override     public Date getResult(ResultSet rs, int columnIndex) throws SQLException {         Timestamp timestamp = rs.getTimestamp(columnIndex);         return timestamp != null ? new Date(timestamp.getTime()) : null;     }        @Override     public Date getResult(CallableStatement cs, int columnIndex) throws SQLException {         Timestamp timestamp = cs.getTimestamp(columnIndex);         return timestamp != null ? new Date(timestamp.getTime()) : null;     } }  // 注册自定义TypeHandler configuration.getTypeHandlerRegistry().register(Date.class, JdbcType.TIMESTAMP, new CustomDateTypeHandler()); 

5. ParameterMapping参数映射配置

5.1 参数映射核心属性

ParameterMapping定义了参数的映射配置信息:

package org.apache.ibatis.mapping;  import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.TypeHandler;  /**  * 参数映射配置类  * 包含参数的详细映射信息  */ public class ParameterMapping {      private Configuration configuration;        // 参数属性名称(在参数对象中的属性名)     private String property;        // 参数模式:IN(输入)、OUT(输出)、INOUT(输入输出)     private ParameterMode mode;        // Java类型     private Class<?> javaType = Object.class;        // JDBC类型     private JdbcType jdbcType;        // 数字精度(用于NUMERIC和DECIMAL类型)     // 例如:BigDecimal类型的小数位数,price字段精度为2表示保留两位小数     // 应用场景:处理金额、汇率等需要精确小数的数值     private Integer numericScale;        // 类型处理器     private TypeHandler<?> typeHandler;        // 结果映射ID(用于复杂类型)     private String resultMapId;        // JDBC类型名称     private String jdbcTypeName;        // 表达式(用于动态参数)     private String expression;      // 构造方法和Getter/Setter略...        /**      * 参数映射建造器      * 使用建造者模式构建ParameterMapping对象      */     public static class Builder {         private ParameterMapping parameterMapping = new ParameterMapping();          public Builder(Configuration configuration, String property, TypeHandler<?> typeHandler) {             parameterMapping.configuration = configuration;             parameterMapping.property = property;             parameterMapping.typeHandler = typeHandler;         }          public Builder mode(ParameterMode mode) {             parameterMapping.mode = mode;             return this;         }          public Builder javaType(Class<?> javaType) {             parameterMapping.javaType = javaType;             return this;         }          public Builder jdbcType(JdbcType jdbcType) {             parameterMapping.jdbcType = jdbcType;             return this;         }          public Builder numericScale(Integer numericScale) {             parameterMapping.numericScale = numericScale;             return this;         }          public Builder resultMapId(String resultMapId) {             parameterMapping.resultMapId = resultMapId;             return this;         }          public Builder jdbcTypeName(String jdbcTypeName) {             parameterMapping.jdbcTypeName = jdbcTypeName;             return this;         }          public Builder expression(String expression) {             parameterMapping.expression = expression;             return this;         }          public ParameterMapping build() {             resolveTypeHandler();             validate();             return parameterMapping;         }          private void resolveTypeHandler() {             if (parameterMapping.typeHandler == null && parameterMapping.javaType != null) {                 Configuration configuration = parameterMapping.configuration;                 TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();                 parameterMapping.typeHandler = typeHandlerRegistry.getTypeHandler(parameterMapping.javaType, parameterMapping.jdbcType);             }         }          private void validate() {             if (parameterMapping.typeHandler == null) {                 throw new BuilderException("Type handler was null on parameter mapping for property '"                     + parameterMapping.property + "'. It was either not specified and/or could not be found for the javaType ("                     + parameterMapping.javaType.getName() + ") : jdbcType (" + parameterMapping.jdbcType + ") combination.");             }         }     } } 

5.2 参数映射创建流程

参数映射通过SqlSource解析过程创建,支持在XML或注解中配置精度:

XML配置示例(指定DECIMAL精度)

<!-- 指定DECIMAL类型的精度 --> <select id="findByPrice" resultType="Product">     SELECT * FROM product      WHERE price = #{price, javaType=BigDecimal, jdbcType=DECIMAL, numericScale=2} </select>  <!-- 批量插入时指定精度 --> <insert id="batchInsert" parameterType="list">     INSERT INTO product (name, price) VALUES     <foreach collection="list" item="item" separator=",">         (#{item.name}, #{item.price, javaType=BigDecimal, jdbcType=DECIMAL, numericScale=2})     </foreach> </insert> 

参数映射构建代码

<!-- 指定DECIMAL类型的精度 --> <select id="findByPrice" resultType="Product">     SELECT * FROM product      WHERE price = #{price, javaType=BigDecimal, jdbcType=DECIMAL, numericScale=2} </select> 

参数映射构建代码

// 在XMLScriptBuilder或AnnotationBuilder中创建ParameterMapping public ParameterMapping buildParameterMapping(Class<?> parameterType, String property, Class<?> javaType, JdbcType jdbcType) {     // 解析参数类型     if (javaType == null) {         if (JdbcType.CURSOR.equals(jdbcType)) {             javaType = java.sql.ResultSet.class;         } else if (Map.class.isAssignableFrom(parameterType)) {             javaType = Object.class;         } else {             MetaClass metaClass = MetaClass.forClass(parameterType, configuration.getReflectorFactory());             javaType = metaClass.getGetterType(property);         }     }        // 获取类型处理器     TypeHandler<?> typeHandler = configuration.getTypeHandlerRegistry().getTypeHandler(javaType, jdbcType);        // 构建ParameterMapping     return new ParameterMapping.Builder(configuration, property, typeHandler)         .javaType(javaType)         .jdbcType(jdbcType)         .build(); } 

6. 不同参数类型的处理策略

6.1 基本类型参数处理

/**  * 基本类型参数处理示例  * 当参数是基本类型时,直接使用parameterObject作为参数值  */ public class BasicParameterExample {        /**      * 单个基本类型参数      * SQL: SELECT * FROM user WHERE id = ?      */     @Test     public void testSingleBasicParameter() throws SQLException {         // 参数对象就是基本类型值         Object parameterObject = 123L;                // MyBatis会检测到Long类型有对应的TypeHandler         boolean hasTypeHandler = typeHandlerRegistry.hasTypeHandler(parameterObject.getClass());         System.out.println("Long类型有TypeHandler: " + hasTypeHandler);                // 直接使用parameterObject作为参数值         if (hasTypeHandler) {             Object value = parameterObject; // 123L             System.out.println("参数值: " + value);         }     }        /**      * 多个基本类型参数      * SQL: SELECT * FROM user WHERE id = ? AND status = ?      * MyBatis会自动包装为Map      */     @Test     public void testMultipleBasicParameters() {         // MyBatis自动包装:{arg0=123, arg1="ACTIVE", param1=123, param2="ACTIVE"}         Map<String, Object> parameterObject = new HashMap<>();         parameterObject.put("arg0", 123L);         parameterObject.put("arg1", "ACTIVE");         parameterObject.put("param1", 123L);         parameterObject.put("param2", "ACTIVE");                // 通过属性名获取值         Object value1 = ((Map) parameterObject).get("arg0"); // 123L         Object value2 = ((Map) parameterObject).get("arg1"); // "ACTIVE"                System.out.println("第一个参数: " + value1);         System.out.println("第二个参数: " + value2);     } } 

6.2 复杂对象参数处理

/**  * 复杂对象参数处理示例  * 当参数是POJO对象时,通过MetaObject反射获取属性值  */ public class ComplexParameterExample {        /**      * POJO对象参数      * SQL: INSERT INTO user (name, email, age) VALUES (?, ?, ?)      */     @Test     public void testPojoParameter() {         // 参数对象是User POJO         User parameterObject = new User();         parameterObject.setName("张三");         parameterObject.setEmail("zhangsan@example.com");         parameterObject.setAge(25);                // MyBatis检测User类型没有内置TypeHandler         boolean hasTypeHandler = typeHandlerRegistry.hasTypeHandler(parameterObject.getClass());         System.out.println("User类型有TypeHandler: " + hasTypeHandler); // false                if (!hasTypeHandler) {             // 创建MetaObject进行反射操作             MetaObject metaObject = configuration.newMetaObject(parameterObject);                        // 通过属性名获取值             Object nameValue = metaObject.getValue("name");       // "张三"             Object emailValue = metaObject.getValue("email");     // "zhangsan@example.com"             Object ageValue = metaObject.getValue("age");         // 25                        System.out.println("name属性值: " + nameValue);             System.out.println("email属性值: " + emailValue);             System.out.println("age属性值: " + ageValue);         }     }        /**      * 嵌套对象参数      * SQL: SELECT * FROM order WHERE user.id = ? AND user.name = ?      */     @Test     public void testNestedObjectParameter() {         // 创建嵌套对象         User user = new User();         user.setId(123L);         user.setName("张三");                Order parameterObject = new Order();         parameterObject.setUser(user);                // 通过MetaObject访问嵌套属性         MetaObject metaObject = configuration.newMetaObject(parameterObject);                Object userId = metaObject.getValue("user.id");     // 123L         Object userName = metaObject.getValue("user.name"); // "张三"                System.out.println("嵌套属性user.id: " + userId);         System.out.println("嵌套属性user.name: " + userName);     } } 

6.3 集合类型参数处理

/**  * 集合类型参数处理示例  * MyBatis通过动态SQL和额外参数处理集合  */ public class CollectionParameterExample {        /**      * List参数处理      * SQL: SELECT * FROM user WHERE id IN <foreach collection="list" item="id" open="(" separator="," close=")">#{id}</foreach>      */     @Test     public void testListParameter() {         // 参数是List集合         List<Long> parameterObject = Arrays.asList(1L, 2L, 3L);                // MyBatis会将List包装为Map: {"list": [1L, 2L, 3L]}         Map<String, Object> wrappedParam = new HashMap<>();         wrappedParam.put("list", parameterObject);                // 在foreach处理过程中,会生成额外参数         BoundSql boundSql = new BoundSql(configuration, "SELECT * FROM user WHERE id IN (?, ?, ?)",              Arrays.asList(                 createParameterMapping("__frch_id_0", Long.class),                 createParameterMapping("__frch_id_1", Long.class),                 createParameterMapping("__frch_id_2", Long.class)             ), wrappedParam);                    // 设置额外参数         boundSql.setAdditionalParameter("__frch_id_0", 1L);         boundSql.setAdditionalParameter("__frch_id_1", 2L);         boundSql.setAdditionalParameter("__frch_id_2", 3L);                // 获取额外参数(最高优先级)         Object value1 = boundSql.getAdditionalParameter("__frch_id_0"); // 1L         Object value2 = boundSql.getAdditionalParameter("__frch_id_1"); // 2L         Object value3 = boundSql.getAdditionalParameter("__frch_id_2"); // 3L                System.out.println("foreach生成的参数1: " + value1);         System.out.println("foreach生成的参数2: " + value2);         System.out.println("foreach生成的参数3: " + value3);     }        /**      * Map参数处理      * SQL: SELECT * FROM user WHERE name = #{name} AND age = #{age}      */     @Test     public void testMapParameter() {         // 参数是Map         Map<String, Object> parameterObject = new HashMap<>();         parameterObject.put("name", "张三");         parameterObject.put("age", 25);                // Map类型没有内置TypeHandler,但Map实现了特殊处理         // Map参数可以直接从boundSql.getAdditionalParameter获取         // 或通过MetaObject的MapWrapper获取         MetaObject metaObject = configuration.newMetaObject(parameterObject);                // 通过Map的key获取值         Object nameValue = metaObject.getValue("name"); // "张三"         Object ageValue = metaObject.getValue("age");   // 25                System.out.println("Map参数name: " + nameValue);         System.out.println("Map参数age: " + ageValue);                // Map作为参数的特殊处理         // 当参数是Map时,MyBatis会将其包装为ParamMap         // SQL中的#{name}会直接从Map中通过"name"键获取值         System.out.println("Map参数直接映射: #{name} -> " + parameterObject.get("name"));     }        /**      * Map参数实际应用示例      */     @Test     public void testMapParameterInAction() {         Map<String, Object> params = new HashMap<>();         params.put("id", 1L);         params.put("name", "张三");         params.put("age", 25);                // 对应的Mapper方法         // List<User> findByMap(Map<String, Object> params);         // SQL: SELECT * FROM user WHERE id = #{id} AND name = #{name} AND age = #{age}                // 实际应用场景:Map参数适合动态条件查询,无需创建专门的参数对象         // MyBatis会直接从Map中获取键对应的值(如id、name、age),         // 并通过MetaObject包装后按属性名访问                System.out.println("使用Map参数查询: " + params);     } } 

7. 实践案例:自定义参数处理器

7.1 参数加密处理器

让我们创建一个参数加密处理器,自动对敏感参数进行加密:

package com.example.parameter;  import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.ParameterMapping; import org.apache.ibatis.mapping.ParameterMode; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.scripting.defaults.DefaultParameterHandler; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.TypeHandler; import org.apache.ibatis.type.TypeHandlerRegistry;  import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Base64; import java.util.HashSet; import java.util.List; import java.util.Set;  /**  * 参数加密处理器  * 对指定的敏感参数进行自动加密  */ public class EncryptionParameterHandler implements ParameterHandler {        private final DefaultParameterHandler delegate;     private final Configuration configuration;     private final TypeHandlerRegistry typeHandlerRegistry;     private final MappedStatement mappedStatement;     private final Object parameterObject;     private final BoundSql boundSql;        // 需要加密的字段名集合     private static final Set<String> ENCRYPT_FIELDS = new HashSet<>();        // AES加密密钥(实际应用中应从配置文件读取)     private static final String ENCRYPT_KEY = "MyBatis123456789"; // 16位密钥        static {         // 配置需要加密的字段         ENCRYPT_FIELDS.add("password");         ENCRYPT_FIELDS.add("idCard");         ENCRYPT_FIELDS.add("phone");         ENCRYPT_FIELDS.add("email");     }        public EncryptionParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {         this.delegate = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);         this.configuration = mappedStatement.getConfiguration();         this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();         this.mappedStatement = mappedStatement;         this.parameterObject = parameterObject;         this.boundSql = boundSql;     }        @Override     public Object getParameterObject() {         return delegate.getParameterObject();     }        @Override     public void setParameters(PreparedStatement ps) throws SQLException {         System.out.println("=== 使用加密参数处理器 ===");                List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();                if (parameterMappings != null) {             MetaObject metaObject = null;                        for (int i = 0; i < parameterMappings.size(); i++) {                 ParameterMapping parameterMapping = parameterMappings.get(i);                                if (parameterMapping.getMode() != ParameterMode.OUT) {                     Object value;                     String propertyName = parameterMapping.getProperty();                                        // 获取原始参数值(使用与DefaultParameterHandler相同的逻辑)                     if (boundSql.hasAdditionalParameter(propertyName)) {                         value = boundSql.getAdditionalParameter(propertyName);                     } else if (parameterObject == null) {                         value = null;                     } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {                         value = parameterObject;                     } else {                         if (metaObject == null) {                             metaObject = configuration.newMetaObject(parameterObject);                         }                         value = metaObject.getValue(propertyName);                     }                                        // 对敏感字段进行加密                     if (needEncryption(propertyName, value)) {                         String originalValue = (String) value;                         value = encryptValue(originalValue);                         System.out.println(String.format("字段 [%s] 加密: %s -> %s",                              propertyName, originalValue, value));                     }                                        // 设置参数                     TypeHandler typeHandler = parameterMapping.getTypeHandler();                     JdbcType jdbcType = parameterMapping.getJdbcType();                                        if (value == null && jdbcType == null) {                         jdbcType = configuration.getJdbcTypeForNull();                     }                                        try {                         typeHandler.setParameter(ps, i + 1, value, jdbcType);                     } catch (Exception e) {                         throw new SQLException("Could not set parameter: " + e.getMessage(), e);                     }                 }             }         }     }        /**      * 判断是否需要加密      */     private boolean needEncryption(String propertyName, Object value) {         return value instanceof String &&                 ENCRYPT_FIELDS.contains(propertyName) &&                 !((String) value).isEmpty();     }        /**      * AES加密      */     private String encryptValue(String value) {         try {             SecretKeySpec secretKey = new SecretKeySpec(ENCRYPT_KEY.getBytes(StandardCharsets.UTF_8), "AES");             Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");             cipher.init(Cipher.ENCRYPT_MODE, secretKey);                        byte[] encryptedBytes = cipher.doFinal(value.getBytes(StandardCharsets.UTF_8));             return Base64.getEncoder().encodeToString(encryptedBytes);         } catch (Exception e) {             throw new RuntimeException("参数加密失败: " + e.getMessage(), e);         }     }        /**      * 添加加密字段      */     public static void addEncryptField(String fieldName) {         ENCRYPT_FIELDS.add(fieldName);     }        /**      * 移除加密字段      */     public static void removeEncryptField(String fieldName) {         ENCRYPT_FIELDS.remove(fieldName);     } } 

7.2 自定义LanguageDriver

创建自定义的LanguageDriver来使用我们的加密参数处理器:

package com.example.parameter;  import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlSource; import org.apache.ibatis.parsing.XNode; import org.apache.ibatis.scripting.LanguageDriver; import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver; import org.apache.ibatis.session.Configuration;  /**  * 加密语言驱动器  * 创建加密参数处理器  */ public class EncryptionLanguageDriver extends XMLLanguageDriver implements LanguageDriver {        @Override     public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {         // 根据配置决定是否使用加密处理器         Configuration configuration = mappedStatement.getConfiguration();                // 检查是否启用加密(可以通过配置或注解控制)         if (isEncryptionEnabled(mappedStatement)) {             System.out.println("创建加密参数处理器 for: " + mappedStatement.getId());             return new EncryptionParameterHandler(mappedStatement, parameterObject, boundSql);         } else {             // 使用默认参数处理器             return super.createParameterHandler(mappedStatement, parameterObject, boundSql);         }     }        /**      * 判断是否启用加密      * 可以根据MappedStatement的ID、配置等进行判断      */     private boolean isEncryptionEnabled(MappedStatement mappedStatement) {         String statementId = mappedStatement.getId();                // 示例:对包含"User"的Mapper启用加密         return statementId.contains("User");     } } 

7.3 配置使用

插件注册方式

自定义ParameterHandler通常通过MyBatis插件机制注册,在 mybatis-config.xml中配置:

<plugins>     <!-- 参数验证拦截器 -->     <plugin interceptor="com.example.ValidationParameterInterceptor"/>     <!-- 参数加密拦截器 -->     <plugin interceptor="com.example.EncryptionParameterInterceptor"/> </plugins> 

语言驱动器配置

<configuration>     <!-- 注册自定义语言驱动器 -->     <typeAliases>         <typeAlias alias="encryptionLanguageDriver" type="com.example.parameter.EncryptionLanguageDriver"/>     </typeAliases>        <!-- 设置默认语言驱动器 -->     <settings>         <setting name="defaultScriptingLanguage" value="encryptionLanguageDriver"/>     </settings>        <!-- 或者在特定的Mapper方法上使用 --> </configuration> 

在Mapper接口中使用:

package com.example.mapper;  import org.apache.ibatis.annotations.*; import com.example.parameter.EncryptionLanguageDriver;  public interface UserMapper {        /**      * 使用默认语言驱动器(会自动加密password字段)      */     @Insert("INSERT INTO user (username, password, email) VALUES (#{username}, #{password}, #{email})")     int insertUser(User user);        /**      * 显式指定语言驱动器      */     @Lang(EncryptionLanguageDriver.class)     @Update("UPDATE user SET password = #{password} WHERE id = #{id}")     int updatePassword(@Param("id") Long id, @Param("password") String password); } 

完整测试代码

package com.example;  import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import com.example.mapper.UserMapper; import com.example.entity.User;  import java.io.InputStream;  /**  * 加密参数处理器测试  * 演示自定义ParameterHandler的完整使用流程  */ public class EncryptionParameterTest {        public static void main(String[] args) {         SqlSessionFactory factory = MyBatisUtils.getSqlSessionFactory();                testEncryptionParameterHandler(factory);     }        /**      * 测试参数加密功能      */     private static void testEncryptionParameterHandler(SqlSessionFactory factory) {         System.out.println("=== 测试参数加密功能 ===");                try (SqlSession session = factory.openSession()) {             UserMapper mapper = session.getMapper(UserMapper.class);                        // 创建用户对象             User user = new User();             user.setUsername("testuser");             user.setPassword("mypassword123");      // 敏感信息,会被加密             user.setEmail("test@example.com");      // 敏感信息,会被加密                        System.out.println(">>> 插入用户前");             System.out.println("原始密码: " + user.getPassword());             System.out.println("原始邮箱: " + user.getEmail());                        // 插入用户(password和email字段会自动加密)             int result = mapper.insertUser(user);                        System.out.println(">>> 插入结果: " + result);                        // 更新密码测试             System.out.println(">>> 更新密码测试");             String newPassword = "newpassword456";             System.out.println("新密码: " + newPassword);                        int updateResult = mapper.updatePassword(1L, newPassword);             System.out.println("更新结果: " + updateResult);                        session.commit();         }     } } 

7.4 参数验证处理器

再创建一个参数验证处理器:

package com.example.parameter;  import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.ParameterMapping; import org.apache.ibatis.mapping.ParameterMode; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.scripting.defaults.DefaultParameterHandler; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.type.TypeHandlerRegistry;  import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Predicate; import java.util.regex.Pattern;  /**  * 参数验证处理器  * 在设置参数前进行数据验证  */ public class ValidationParameterHandler implements ParameterHandler {        private final DefaultParameterHandler delegate;     private final Configuration configuration;     private final TypeHandlerRegistry typeHandlerRegistry;     private final MappedStatement mappedStatement;     private final Object parameterObject;     private final BoundSql boundSql;        // 验证规则映射     private static final Map<String, Predicate<Object>> VALIDATION_RULES = new HashMap<>();        static {         // 邮箱格式验证         VALIDATION_RULES.put("email", value -> {             if (value == null) return true;             String email = value.toString();             return Pattern.matches("^[A-Za-z0-9+_.-]+@([A-Za-z0-9.-]+\.[A-Za-z]{2,})$", email);         });                // 手机号格式验证         VALIDATION_RULES.put("phone", value -> {             if (value == null) return true;             String phone = value.toString();             return Pattern.matches("^1[3-9]\d{9}$", phone);         });                // 用户名长度验证         VALIDATION_RULES.put("username", value -> {             if (value == null) return false;             String username = value.toString();             return username.length() >= 3 && username.length() <= 20;         });                // 密码强度验证         VALIDATION_RULES.put("password", value -> {             if (value == null) return false;             String password = value.toString();             // 至少8位,包含字母和数字             return password.length() >= 8 &&                     Pattern.matches(".*[A-Za-z].*", password) &&                     Pattern.matches(".*\d.*", password);         });     }        public ValidationParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {         this.delegate = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);         this.configuration = mappedStatement.getConfiguration();         this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();         this.mappedStatement = mappedStatement;         this.parameterObject = parameterObject;         this.boundSql = boundSql;     }        @Override     public Object getParameterObject() {         return delegate.getParameterObject();     }        @Override     public void setParameters(PreparedStatement ps) throws SQLException {         System.out.println("=== 使用验证参数处理器 ===");                // 首先进行参数验证         validateParameters();                // 验证通过后,委托给默认处理器         delegate.setParameters(ps);     }        /**      * 验证所有参数      */     private void validateParameters() {         List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();                if (parameterMappings != null) {             MetaObject metaObject = null;                        for (ParameterMapping parameterMapping : parameterMappings) {                 if (parameterMapping.getMode() != ParameterMode.OUT) {                     Object value;                     String propertyName = parameterMapping.getProperty();                                        // 获取参数值                     if (boundSql.hasAdditionalParameter(propertyName)) {                         value = boundSql.getAdditionalParameter(propertyName);                     } else if (parameterObject == null) {                         value = null;                     } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {                         value = parameterObject;                     } else {                         if (metaObject == null) {                             metaObject = configuration.newMetaObject(parameterObject);                         }                         value = metaObject.getValue(propertyName);                     }                                        // 执行验证                     validateParameter(propertyName, value);                 }             }         }     }        /**      * 验证单个参数      */     private void validateParameter(String propertyName, Object value) {         Predicate<Object> validator = VALIDATION_RULES.get(propertyName);                if (validator != null) {             boolean isValid = validator.test(value);                        if (!isValid) {                 String message = String.format("参数验证失败: %s = %s", propertyName, value);                 System.err.println(message);                 throw new IllegalArgumentException(message);             } else {                 System.out.println(String.format("参数验证通过: %s = %s", propertyName, value));             }         }     }        /**      * 添加验证规则      */     public static void addValidationRule(String propertyName, Predicate<Object> validator) {         VALIDATION_RULES.put(propertyName, validator);     }        /**      * 移除验证规则      */     public static void removeValidationRule(String propertyName) {         VALIDATION_RULES.remove(propertyName);     } } 

8. 源码调试指导

8.1 关键断点位置

DefaultParameterHandler断点

  1. DefaultParameterHandler.setParameters() - 参数设置入口
  2. 参数值获取的优先级判断逻辑
  3. typeHandler.setParameter() - 类型处理器设置参数

ParameterMapping断点

  1. ParameterMapping.Builder.build() - 参数映射构建
  2. resolveTypeHandler() - 类型处理器解析

MetaObject断点

  1. MetaObject.getValue() - 反射获取属性值
  2. 嵌套属性访问逻辑

8.2 调试技巧

工具建议

  • 使用IDEA的条件断点,表达式:parameterObject.getClass().getSimpleName().equals("User")
  • 使用IDEA的字段监视(Field Watchpoint)观察 parameterObject的变化
  • 启用MyBatis日志:<setting name="logImpl" value="STDOUT_LOGGING"/>

观察参数类型判断

// 在setParameters方法中观察类型判断逻辑 if (boundSql.hasAdditionalParameter(propertyName)) {     System.out.println("使用额外参数: " + propertyName); } else if (parameterObject == null) {     System.out.println("参数对象为null"); } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {     System.out.println("参数是基本类型: " + parameterObject.getClass().getSimpleName()); } else {     System.out.println("参数是复杂对象: " + parameterObject.getClass().getSimpleName()); } 

观察参数映射

// 观察参数映射配置 for (ParameterMapping pm : boundSql.getParameterMappings()) {     System.out.println(String.format("参数映射: property=%s, javaType=%s, jdbcType=%s",          pm.getProperty(), pm.getJavaType().getSimpleName(), pm.getJdbcType())); } 

观察类型处理器选择逻辑

// 在TypeHandlerRegistry.getTypeHandler方法设置断点 // 观察类型处理器的匹配过程 TypeHandler typeHandler = parameterMapping.getTypeHandler(); System.out.println("使用的TypeHandler: " + typeHandler.getClass().getSimpleName());  // 观察TypeHandler的选择逻辑 TypeHandler<?> handler = typeHandlerRegistry.getTypeHandler(     parameterMapping.getJavaType(),      parameterMapping.getJdbcType() ); System.out.println(String.format("类型匹配: javaType=%s, jdbcType=%s -> %s",      parameterMapping.getJavaType().getSimpleName(),     parameterMapping.getJdbcType(),     handler.getClass().getSimpleName())); 

9. 易错与排错清单

9.1 常见问题

问题 原因 解决方案
参数设置失败 ParameterMapping配置错误 检查参数映射的javaType和jdbcType
类型转换异常 TypeHandler选择不当 确认类型处理器配置
参数值为null 属性名拼写错误或对象为null 检查属性名和参数对象
反射失败 私有属性无getter方法 确保属性有对应的getter方法
嵌套属性访问失败 中间对象为null 检查嵌套对象的初始化
参数名不可靠 无@Param且未开启-parameters编译选项 使用@Param注解或启用编译参数保留
动态SQL参数错误 <foreach>的collection配置错误导致参数缺失 检查collection属性与实际参数类型匹配(list/array/map key),使用@Param明确命名避免歧义

9.2 性能优化建议

  1. 复用TypeHandler:为常用类型注册TypeHandler,避免每次查询都创建新实例
  2. 避免过度反射:复杂对象参数尽量使用字段直接访问,减少getter调用。MetaObject缓存可减少10-15%反射开销,通过 Reflector缓存属性元数据避免重复解析
  3. 批量操作优化:使用 executorType="BATCH"配合 addBatch,减少参数设置次数
  4. 参数命名规范:避免使用过长的嵌套属性(如 user.address.city.name),层级过深会增加反射开销

10. 小结

恭喜你!看到这里,你已经彻底搞懂ParameterHandler这个"参数翻译官"是怎么工作的了。

让我们快速回顾一下今天的收获

  1. ParameterHandler接口:简洁到只有2个方法,但设计得很优雅
  2. DefaultParameterHandler:内置的"四级参数查找机制",从额外参数到反射,层层递进
  3. ParameterMapping:参数的"身份证",记录了参数的所有信息
  4. 参数类型处理:基本类型直接用、复杂对象靠反射、集合类型走额外参数
  5. TypeHandler协作:ParameterHandler找值,TypeHandler转类型,分工明确
  6. 扩展开发:想加密?想验证?写个ParameterHandler实现类就完事儿

三个关键认知

  • 类型安全是根本:TypeHandler确保类型转换不出错,这是底线
  • 性能优化有门道:优先级策略、MetaObject缓存、Reflector复用,都是为了快
  • 灵活扩展是王道:加密、验证、日志...想玩什么花样都行

一句话总结
ParameterHandler就像是一个靠谱的翻译官,它知道怎么把你的Java对象"翻译"成数据库能理解的参数。有了它,我们写代码时只管传对象,剩下的脏活累活它全包了!

小彩蛋
下一篇我们要学ResultSetHandler了,如果说ParameterHandler是"往数据库送东西",那ResultSetHandler就是"从数据库拿东西"。你猜猜它会用什么招数把ResultSet转成Java对象?😏

在下一篇文章中,我们将深入分析ResultSetHandler结果集处理机制,了解SQL查询结果的处理和对象映射过程。


思考题

  1. DefaultParameterHandler的参数值获取为什么要设计优先级策略?各个优先级的应用场景是什么?
  2. ParameterHandler如何与TypeHandler协作完成类型转换?为什么要这样设计?
  3. 在什么情况下会产生额外参数(AdditionalParameter)?它们是如何生成和使用的?
  4. 如何设计一个通用的参数处理器来支持多种扩展功能(如加密、验证、日志等)?
发表评论

评论已关闭。

相关文章