MyBatis?ParameterHandler的具體使用
一、概述
書接上回,StatementHandler參數(shù)處理&結(jié)果處理,分別委托給ParameterHandler&ResultSetHandler(注意不是ResultHandler)。那么本文介紹一下ParameterHandler。
本系列的第二章,我們講了Mybatis的參數(shù)處理,那個(gè)參數(shù)處理,是把Mapper中各種可能的參數(shù)封裝為Map,而這個(gè)參數(shù)處理,是把Map參數(shù),設(shè)置到JDBC的Statement中。
二、源碼
MyBatis中ParameterHandler只有一個(gè)實(shí)現(xiàn)DefaultParameterHandler,我們通過源碼來解析其工作原理。
1 關(guān)鍵屬性
DefaultParameterHandler 的關(guān)鍵屬性:
MappedStatement:包含SQL元信息;parameterObject:Mapper方法傳入的原始參數(shù);BoundSql:包含解析后的 SQL 語句和參數(shù)映射列表;TypeHandlerRegistry:類型處理器注冊(cè)表,用于查找合適的 TypeHandler。
public class DefaultParameterHandler implements ParameterHandler {
private final MappedStatement mappedStatement;
private final Object parameterObject;
private final BoundSql boundSql;
private final Configuration configuration;
private final TypeHandlerRegistry typeHandlerRegistry;
public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
this.mappedStatement = mappedStatement;
this.parameterObject = parameterObject;
this.boundSql = boundSql;
this.configuration = mappedStatement.getConfiguration();
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
}
}
2.setParameters
setParameters 是ParameterHandler的核心方法,負(fù)責(zé)將參數(shù)綁定到 PreparedStatement 中:
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
// 獲取SQL中的參數(shù)映射列表(每個(gè)元素對(duì)應(yīng)一個(gè)占位符)
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
// 遍歷每個(gè)參數(shù)映射,依次綁定到PreparedStatement
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
// 忽略輸出參數(shù)(存儲(chǔ)過程場(chǎng)景)
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty(); // 參數(shù)名稱(如"id"、"user.name")
// 獲取參數(shù)值:從原始參數(shù)對(duì)象中解析propertyName對(duì)應(yīng)的 value
if (boundSql.hasAdditionalParameter(propertyName)) {
// 從附加參數(shù)中獲取(<foreach>或<bind>標(biāo)簽可以提供額外參數(shù))
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null; // 參數(shù)為null時(shí)直接設(shè)為null
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
// 參數(shù)本身就是TypeHandler可以處理的類型
value = parameterObject;
} else {
// 參數(shù)是復(fù)雜對(duì)象,通過反射獲取propertyName對(duì)應(yīng)的屬性值
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
// 獲取參數(shù)對(duì)應(yīng)的TypeHandler(負(fù)責(zé)Java類型→JDBC類型的轉(zhuǎn)換)
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
// 調(diào)用TypeHandler的方法,將value綁定到PreparedStatement的第i+1個(gè)占位符
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
這段代碼的執(zhí)行流程可分為三步:
- 解析參數(shù)映射列表(
ParameterMapping),每個(gè)元素對(duì)應(yīng) SQL 中的一個(gè)占位符 - 根據(jù)參數(shù)名稱從參數(shù)對(duì)象中獲取參數(shù)值(支持復(fù)雜對(duì)象的屬性訪問)
- 通過 TypeHandler 將參數(shù)值轉(zhuǎn)換為 JDBC 類型并綁定到 PreparedStatement
AdditionalParameter這個(gè)額外參數(shù)是什么?
- foreach標(biāo)簽中的item、index都會(huì)被解析為額外參數(shù);
- bind標(biāo)簽,也可以提供額外參數(shù),你可以利用bind標(biāo)簽,使用OGNL表達(dá)式以外創(chuàng)建一個(gè)變量,并將其綁定到當(dāng)前的上下文。
3.TypeHandler
TypeHandler(類型處理器)負(fù)責(zé)Java類型與JDBC類型之間的轉(zhuǎn)換,不只是ParameterHandler處理參數(shù)需要他,ResultSetHandler處理返回結(jié)果時(shí)也需要他。
1.TypeHandler接口定義
通過接口定義就可以看出來,TypeHandler負(fù)責(zé)JAVA類型與JDBC類型互相轉(zhuǎn)換。
public interface TypeHandler<T> {
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
T getResult(ResultSet rs, String columnName) throws SQLException;
T getResult(ResultSet rs, int columnIndex) throws SQLException;
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
2. 內(nèi)置TypeHandler體系
MyBatis 提供了豐富的內(nèi)置 TypeHandler,這里的例子只是冰山一角,有興趣可以看下源碼,一般情況下,是不需要自己自定義類型轉(zhuǎn)換器的。具體實(shí)現(xiàn)這里不深入了,比較簡(jiǎn)單,模板模式,BaseTypeHandler實(shí)現(xiàn)了一些通用邏輯,子類就是JDBC API的各種調(diào)用。有興趣可以直接看源碼。
| 類型分類 | 典型實(shí)現(xiàn)類 | 功能說明 |
|---|---|---|
| 基本類型 | IntegerTypeHandler | 處理 Integer 與 JDBC INTEGER 轉(zhuǎn)換 |
| 字符串類型 | StringTypeHandler | 處理 String 與 JDBC VARCHAR 轉(zhuǎn)換 |
| 日期類型 | LocalDateTimeTypeHandler | 處理 Java 8 日期類型與 JDBC 日期類型轉(zhuǎn)換 |
| 枚舉類型 | EnumTypeHandler | 處理枚舉的名稱與 JDBC 類型轉(zhuǎn)換 |
| 集合 / 數(shù)組類型 | ArrayTypeHandler | 處理 Java 數(shù)組與 JDBC ARRAY 類型轉(zhuǎn)換 |
4. JdbcTypeForNull 的作用
jdbcTypeForNull是 MyBatis中用于處理null值參數(shù)的重要配置,當(dāng)參數(shù)值為 null且未指定具體JDBC 類型時(shí)生效。
其主要作用是解決不同數(shù)據(jù)庫對(duì) null 值處理的兼容性問題,某些數(shù)據(jù)庫,對(duì) null 值的類型非常敏感,需要明確指定 JDBC 類型,比如Oracle,需要設(shè)置為NULL。
可以在xml中配置
<configuration>
<settings>
<!-- 設(shè)置null值默認(rèn)的JDBC類型 -->
<setting name="jdbcTypeForNull" value="NULL"/>
</settings>
</configuration>
三、小結(jié)
ParameterHandler是MyBatis處理Statement的參數(shù)的核心組件,通過與 TypeHandler協(xié)作,實(shí)現(xiàn)了Java 類型到JDBC類型的靈活轉(zhuǎn)換,支撐了MyBatis對(duì)各種復(fù)雜參數(shù)場(chǎng)景的處理能力。
到此這篇關(guān)于MyBatis ParameterHandler的具體使用的文章就介紹到這了,更多相關(guān)MyBatis ParameterHandler內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- MyBatis中傳入?yún)?shù)parameterType類型詳解
- Mybatis非配置原因,導(dǎo)致SqlSession was not registered for synchronization異常
- mybatis中批量插入的兩種方式(高效插入)
- Mybatis中使用in()查詢的方式詳解
- Mybatis Update操作返回值問題
- MyBatis入門學(xué)習(xí)教程(一)-MyBatis快速入門
- MyBatis批量插入(insert)數(shù)據(jù)操作
- Mybatis plus實(shí)現(xiàn)Distinct去重功能
- Mybatis報(bào)錯(cuò): org.apache.ibatis.exceptions.PersistenceException解決辦法
相關(guān)文章
SpringBoot使用Jwt處理跨域認(rèn)證問題的教程詳解
這篇文章主要介紹了SpringBoot使用Jwt處理跨域認(rèn)證問題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06
SpringBoot異步調(diào)用方法實(shí)現(xiàn)場(chǎng)景代碼實(shí)例
這篇文章主要介紹了SpringBoot異步調(diào)用方法實(shí)現(xiàn)場(chǎng)景代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
Mybatis使用foreach批量插入Oracle提示命令未正確結(jié)束方式
文章主要討論了在Oracle和MySQL中使用foreach標(biāo)簽時(shí)的語法差異,以及foreach元素的屬性介紹,包括item、index、collection、open、separator和close等2025-11-11
Java中Map與對(duì)象之間互相轉(zhuǎn)換的幾種常用方式
在Java中將對(duì)象和Map相互轉(zhuǎn)換是常見的操作,可以通過不同的方式實(shí)現(xiàn)這種轉(zhuǎn)換,下面這篇文章主要給大家介紹了關(guān)于Java中Map與對(duì)象之間互相轉(zhuǎn)換的幾種常用方式,需要的朋友可以參考下2024-01-01
springboot使用Thymeleaf報(bào)錯(cuò)常見的幾種解決方案
這篇文章主要介紹了springboot使用Thymeleaf報(bào)錯(cuò)常見的幾種解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11
java發(fā)送http請(qǐng)求并獲取狀態(tài)碼的簡(jiǎn)單實(shí)例
下面小編就為大家?guī)硪黄猨ava發(fā)送http請(qǐng)求并獲取狀態(tài)碼的簡(jiǎn)單實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-05-05

