最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

MyBatis參數(shù)處理模塊用法及解讀

 更新時(shí)間:2026年01月01日 16:20:16   作者:雨中飄蕩的記憶  
MyBatis參數(shù)處理模塊是核心處理層的關(guān)鍵環(huán)節(jié),負(fù)責(zé)將Java對(duì)象參數(shù)轉(zhuǎn)換為JDBC參數(shù)類(lèi)型,并設(shè)置到PreparedStatement中,主要職責(zé)包括參數(shù)設(shè)置流程、參數(shù)類(lèi)型轉(zhuǎn)換和參數(shù)映射處理

一、MyBatis整體架構(gòu)與參數(shù)處理模塊

在深入?yún)?shù)處理模塊之前,我們先了解MyBatis的整體架構(gòu),以及參數(shù)處理模塊在其中的重要地位。

從上圖可以看出,MyBatis采用了分層架構(gòu)設(shè)計(jì),而參數(shù)處理模塊位于核心處理層,是SQL執(zhí)行過(guò)程中的關(guān)鍵環(huán)節(jié)。它負(fù)責(zé)將Java對(duì)象參數(shù)轉(zhuǎn)換為JDBC能夠識(shí)別的參數(shù)類(lèi)型,并設(shè)置到PreparedStatement中。

1.1 參數(shù)處理模塊的核心職責(zé)

參數(shù)處理模塊主要承擔(dān)以下核心職責(zé):

? 設(shè)置SQL參數(shù)將Java對(duì)象參數(shù)設(shè)置到PreparedStatement的占位符中
? 參數(shù)類(lèi)型轉(zhuǎn)換通過(guò)TypeHandler完成Java類(lèi)型與JDBC類(lèi)型的轉(zhuǎn)換
? 處理參數(shù)映射根據(jù)ParameterMapping解析參數(shù)的名稱、類(lèi)型和處理方式
? 處理Null值對(duì)Null值進(jìn)行特殊處理,包括JdbcType的指定
? 存儲(chǔ)過(guò)程支持支持CallableStatement的參數(shù)注冊(cè)

1.2 ParameterHandler接口

ParameterHandler是參數(shù)處理的頂層接口,定義了參數(shù)處理的基本方法:

public interface ParameterHandler {
    // 獲取參數(shù)對(duì)象
    Object getParameterObject();

    // 設(shè)置PreparedStatement參數(shù)
    void setParameters(PreparedStatement ps) 
        throws SQLException;
}

二、ParameterHandler架構(gòu)

ParameterHandler采用了簡(jiǎn)單但高效的設(shè)計(jì)模式。

2.1 默認(rèn)實(shí)現(xiàn)類(lèi)

MyBatis提供了ParameterHandler的默認(rèn)實(shí)現(xiàn)——DefaultParameterHandler:

public class DefaultParameterHandler 
    implements ParameterHandler {

    private final TypeHandlerRegistry typeHandlerRegistry;
    private final MappedStatement mappedStatement;
    private final Object parameterObject;
    private final BoundSql boundSql;
    private final Configuration configuration;

    @Override
    public void setParameters(PreparedStatement ps) {
        // 獲取參數(shù)映射列表
        List<ParameterMapping> parameterMappings = 
            boundSql.getParameterMappings();

        if (parameterMappings != null) {
            // 遍歷并設(shè)置每個(gè)參數(shù)
            for (int i = 0; i < parameterMappings.size(); i++) {
                ParameterMapping parameterMapping = 
                    parameterMappings.get(i);

                // 獲取參數(shù)值并設(shè)置
                Object value = getParameterValue(parameterMapping);
                TypeHandler typeHandler = 
                    parameterMapping.getTypeHandler();
                typeHandler.setParameter(ps, i + 1, value, jdbcType);
            }
        }
    }
}

2.2 創(chuàng)建ParameterHandler

ParameterHandler通常由Configuration創(chuàng)建:

public ParameterHandler newParameterHandler(
    MappedStatement mappedStatement,
    Object parameterObject,
    BoundSql boundSql) {

    ParameterHandler parameterHandler = 
        mappedStatement.getLang()
            .createParameterHandler(
                mappedStatement, 
                parameterObject, 
                boundSql);

    // 應(yīng)用插件攔截
    parameterHandler = (ParameterHandler) 
        interceptorChain.pluginAll(parameterHandler);

    return parameterHandler;
}

三、參數(shù)設(shè)置流程

參數(shù)設(shè)置是一個(gè)系統(tǒng)化的過(guò)程,涉及多個(gè)組件的協(xié)同工作

3.1 完整設(shè)置流程

參數(shù)設(shè)置的完整流程如下:

// 步驟1: Executor創(chuàng)建StatementHandler
StatementHandler statementHandler = 
    new RoutingStatementHandler(
        executor, 
        mappedStatement, 
        parameterObject, 
        rowBounds, 
        resultHandler, 
        boundSql);

// 步驟2: StatementHandler創(chuàng)建PreparedStatement
Statement statement = instantiateStatement(connection);

// 步驟3: ParameterHandler設(shè)置參數(shù)
parameterHandler.setParameters(preparedStatement);

3.2 參數(shù)獲取策略

參數(shù)值的獲取采用多種策略:

策略1: 從BoundSql的附加參數(shù)中獲取

if (boundSql.hasAdditionalParameter(propertyName)) {
    return boundSql.getAdditionalParameter(propertyName);
}

策略2: 參數(shù)對(duì)象本身

if (parameterObject == null) {
    return null;
}

策略3: 參數(shù)對(duì)象是單個(gè)基本類(lèi)型

if (typeHandlerRegistry.hasTypeHandler(
    parameterObject.getClass())) {
    return parameterObject;
}

策略4: 從參數(shù)對(duì)象中獲取屬性值

MetaObject metaObject = 
    configuration.newMetaObject(parameterObject);
return metaObject.getValue(propertyName);

3.3 參數(shù)示例

// 示例1: 基本類(lèi)型參數(shù)
User selectById(Long id);
// parameterObject = 1L

// 示例2: 多參數(shù)
User selectByNameAndEmail(
    @Param("name") String name, 
    @Param("email") String email);
// parameterObject = {name: "張三", email: "xxx@example.com"}

// 示例3: POJO參數(shù)
User insert(User user);
// 通過(guò)MetaObject反射獲取user對(duì)象屬性

// 示例4: 集合參數(shù)
List<User> selectByIds(List<Long> ids);
// foreach處理,生成多個(gè)參數(shù)

四、參數(shù)類(lèi)型轉(zhuǎn)換

參數(shù)類(lèi)型轉(zhuǎn)換是ParameterHandler的核心功能,通過(guò)TypeHandler實(shí)現(xiàn)。

4.1 TypeHandler接口

TypeHandler是類(lèi)型轉(zhuǎn)換的核心接口:

public interface TypeHandler<T> {
    // 設(shè)置PreparedStatement參數(shù)
    void setParameter(
        PreparedStatement ps, 
        int i, 
        T parameter, 
        JdbcType jdbcType) throws SQLException;

    // 獲取ResultSet結(jié)果
    T getResult(ResultSet rs, String columnName) 
        throws SQLException;

    T getResult(ResultSet rs, int columnIndex) 
        throws SQLException;

    // 獲取CallableStatement結(jié)果
    T getResult(CallableStatement cs, int columnIndex) 
        throws SQLException;
}

4.2 BaseTypeHandler抽象類(lèi)

為了簡(jiǎn)化TypeHandler的實(shí)現(xiàn),MyBatis提供了BaseTypeHandler抽象類(lèi)

public abstract class BaseTypeHandler<T> 
    implements TypeHandler<T> {

    @Override
    public void setParameter(
        PreparedStatement ps, 
        int i, 
        T parameter, 
        JdbcType jdbcType) throws SQLException {

        if (parameter == null) {
            // 處理null值
            if (jdbcType == null) {
                throw new TypeException(
                    "JDBC requires JdbcType for null parameters");
            }
            ps.setNull(i, jdbcType.TYPE_CODE);
        } else {
            // 設(shè)置非null參數(shù)
            setNonNullParameter(ps, i, parameter, jdbcType);
        }
    }

    // 子類(lèi)實(shí)現(xiàn)具體的類(lèi)型轉(zhuǎn)換邏輯
    protected abstract void setNonNullParameter(
        PreparedStatement ps, 
        int i, 
        T parameter, 
        JdbcType jdbcType) throws SQLException;
}

4.3 常用TypeHandler實(shí)現(xiàn)

StringTypeHandler

public class StringTypeHandler extends BaseTypeHandler<String> {
    @Override
    public void setNonNullParameter(
        PreparedStatement ps, 
        int i, 
        String parameter, 
        JdbcType jdbcType) throws SQLException {
        ps.setString(i, parameter);
    }

    @Override
    public String getNullableResult(
        ResultSet rs, 
        String columnName) throws SQLException {
        return rs.getString(columnName);
    }
}

LongTypeHandler

public class LongTypeHandler extends BaseTypeHandler<Long> {
    @Override
    public void setNonNullParameter(
        PreparedStatement ps, 
        int i, 
        Long parameter, 
        JdbcType jdbcType) throws SQLException {
        ps.setLong(i, parameter);
    }

    @Override
    public Long getNullableResult(
        ResultSet rs, 
        String columnName) throws SQLException {
        long result = rs.getLong(columnName);
        return result == 0 && rs.wasNull() ? null : result;
    }
}

DateTypeHandler

public class DateTypeHandler extends BaseTypeHandler<Date> {
    @Override
    public void setNonNullParameter(
        PreparedStatement ps, 
        int i, 
        Date parameter, 
        JdbcType jdbcType) throws SQLException {
        ps.setTimestamp(i, new Timestamp(parameter.getTime()));
    }

    @Override
    public Date getNullableResult(
        ResultSet rs, 
        String columnName) throws SQLException {
        Timestamp timestamp = rs.getTimestamp(columnName);
        return timestamp == null ? null 
            : new Date(timestamp.getTime());
    }
}

4.4 類(lèi)型注冊(cè)機(jī)制

TypeHandlerRegistry負(fù)責(zé)管理所有TypeHandler:

public class TypeHandlerRegistry {
    // JDBC類(lèi)型到TypeHandler的映射
    private final Map<JdbcType, TypeHandler<?>> 
        jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class);

    // Java類(lèi)型到TypeHandler的映射
    private final Map<Type, Map<JdbcType, TypeHandler<?>>> 
        typeHandlerMap = new HashMap<>();

    // 注冊(cè)TypeHandler
    public <T> void register(
        Class<T> javaType, 
        TypeHandler<? extends T> typeHandler) {
        Map<JdbcType, TypeHandler<?>> map = 
            typeHandlerMap.get(javaType);
        if (map == null) {
            map = new HashMap<>();
            typeHandlerMap.put(javaType, map);
        }
        map.put(null, typeHandler);
    }

    // 獲取TypeHandler
    public <T> TypeHandler<T> getTypeHandler(
        Class<T> type, 
        JdbcType jdbcType) {
        Map<JdbcType, TypeHandler<?>> map = 
            typeHandlerMap.get(type);
        if (map == null) return null;

        TypeHandler<?> handler = map.get(jdbcType);
        if (handler == null) {
            handler = map.get(null);
        }
        return (TypeHandler<T>) handler;
    }
}

五、參數(shù)映射處理

ParameterMapping是參數(shù)映射的核心數(shù)據(jù)結(jié)構(gòu)。

5.1 ParameterMapping結(jié)構(gòu)

public class ParameterMapping {
    private final String property;      // 參數(shù)屬性名
    private final ParameterMode mode;   // 參數(shù)模式(IN/OUT/INOUT)
    private final Class<?> javaType;    // Java類(lèi)型
    private final JdbcType jdbcType;    // JDBC類(lèi)型
    private final TypeHandler<?> typeHandler; // 類(lèi)型處理器
    private final String resultMapId;   // 結(jié)果映射ID
    private final Integer numericScale; // 數(shù)值精度
}

5.2 ParameterMode枚舉

public enum ParameterMode {
    IN,    // 輸入?yún)?shù)
    OUT,   // 輸出參數(shù)
    INOUT  // 輸入輸出參數(shù)
}

5.3 參數(shù)映射解析

參數(shù)映射在SQL解析階段創(chuàng)建:

// SqlSourceBuilder中
public SqlSource parse(
    String originalSql, 
    Class<?> parameterType, 
    Map<String, Object> additionalParameters) {

    // 創(chuàng)建Token處理器
    ParameterMappingTokenHandler handler = 
        new ParameterMappingTokenHandler(
            configuration, 
            parameterType, 
            additionalParameters);

    // 解析#{}占位符
    GenericTokenParser parser = 
        new GenericTokenParser("#{", "}", handler);
    String sql = parser.parse(originalSql);

    // 創(chuàng)建StaticSqlSource
    return new StaticSqlSource(
        configuration, 
        sql, 
        handler.getParameterMappings());
}

5.4 參數(shù)映射示例

<!-- 示例1: 基本參數(shù)映射 -->
<select id="selectById" resultType="User">
    SELECT * FROM t_user WHERE id = #{id}
</select>
<!-- ParameterMapping: 
     {property: id, javaType: Long, jdbcType: BIGINT} -->

<!-- 示例2: 指定jdbcType -->
<insert id="insert">
    INSERT INTO t_user (name, email, create_time)
    VALUES (
        #{name}, 
        #{email, jdbcType=VARCHAR}, 
        #{createTime, jdbcType=TIMESTAMP}
    )
</insert>

<!-- 示例3: 指定typeHandler -->
<insert id="insert">
    INSERT INTO t_user (data)
    VALUES (#{data, typeHandler=com.example.JsonTypeHandler})
</insert>

<!-- 示例4: 存儲(chǔ)過(guò)程參數(shù) -->
<select id="callProcedure" statementType="CALLABLE">
    {call get_user_info(
        #{userId, mode=IN, jdbcType=BIGINT},
        #{userName, mode=OUT, jdbcType=VARCHAR},
        #{userEmail, mode=OUT, jdbcType=VARCHAR}
    )}
</select>

六、TypeHandler體系

MyBatis提供了豐富的TypeHandler實(shí)現(xiàn),覆蓋了常見(jiàn)的Java類(lèi)型和JDBC類(lèi)型

6.1 內(nèi)置TypeHandler

MyBatis內(nèi)置的TypeHandler包括:

Java類(lèi)型JDBC類(lèi)型TypeHandler
BooleanBITBooleanTypeHandler
ByteTINYINTByteTypeHandler
ShortSMALLINTShortTypeHandler
IntegerINTEGERIntegerTypeHandler
LongBIGINTLongTypeHandler
FloatFLOATFloatTypeHandler
DoubleDOUBLEDoubleTypeHandler
StringVARCHARStringTypeHandler
byte[]BLOBBlobTypeHandler
DateTIMESTAMPDateTypeHandler
BigDecimalDECIMALBigDecimalTypeHandler

6.2 自定義TypeHandler

當(dāng)內(nèi)置TypeHandler無(wú)法滿足需求時(shí),可以自定義TypeHandler:

// 示例: JSON類(lèi)型處理器
@MappedTypes(List.class)
@MappedJdbcTypes(JdbcType.VARCHAR)
public class JsonListTypeHandler 
    extends BaseTypeHandler<List<String>> {

    private static final Gson GSON = new Gson();

    @Override
    public void setNonNullParameter(
        PreparedStatement ps, 
        int i, 
        List<String> parameter, 
        JdbcType jdbcType) throws SQLException {
        ps.setString(i, GSON.toJson(parameter));
    }

    @Override
    public List<String> getNullableResult(
        ResultSet rs, 
        String columnName) throws SQLException {
        String json = rs.getString(columnName);
        return json == null ? null 
            : GSON.fromJson(json, List.class);
    }

    @Override
    public List<String> getNullableResult(
        ResultSet rs, 
        int columnIndex) throws SQLException {
        String json = rs.getString(columnIndex);
        return json == null ? null 
            : GSON.fromJson(json, List.class);
    }

    @Override
    public List<String> getNullableResult(
        CallableStatement cs, 
        int columnIndex) throws SQLException {
        String json = cs.getString(columnIndex);
        return json == null ? null 
            : GSON.fromJson(json, List.class);
    }
}

6.3 注冊(cè)自定義TypeHandler

配置自定義TypeHandler有兩種方式:

方式1: 注解方式

@MappedTypes(List.class)
@MappedJdbcTypes(JdbcType.VARCHAR)
public class JsonListTypeHandler 
    extends BaseTypeHandler<List<String>> {
    // 實(shí)現(xiàn)代碼...
}

方式2: 配置文件方式

<typeHandlers>
    <typeHandler 
        handler="com.example.JsonListTypeHandler"/>
</typeHandlers>

或指定Java類(lèi)型:

<typeHandlers>
    <typeHandler 
        javaType="java.util.List"
        jdbcType="VARCHAR"
        handler="com.example.JsonListTypeHandler"/>
</typeHandlers>

七、特殊參數(shù)處理

7.1 Null值處理

對(duì)于Null值,需要指定JdbcType:

<!-- 錯(cuò)誤寫(xiě)法: 可能報(bào)錯(cuò) -->
UPDATE t_user SET name = #{name}

<!-- 正確寫(xiě)法: 指定jdbcType -->
UPDATE t_user SET name = #{name, jdbcType=VARCHAR}

<!-- 全局配置: 指定Null的默認(rèn)JdbcType -->
<settings>
    <setting name="jdbcTypeForNull" value="NULL"/>
</settings>

7.2 存儲(chǔ)過(guò)程參數(shù)

存儲(chǔ)過(guò)程支持IN、OUT、INOUT三種參數(shù)模式:

// Mapper接口
void callProcedure(
    @Param("userId") Long userId,
    @Param("userName") String userName,
    @Param("result") Integer result);

// XML配置
<select id="callProcedure" statementType="CALLABLE">
    {call calculate_discount(
        #{userId, mode=IN, jdbcType=BIGINT},
        #{userName, mode=IN, jdbcType=VARCHAR},
        #{result, mode=OUT, jdbcType=INTEGER}
    )}
</select>

7.3 數(shù)組參數(shù)處理

// Mapper接口
List<User> selectByIds(Long[] ids);

// XML配置
<select id="selectByIds" resultType="User">
    SELECT * FROM t_user
    WHERE id IN
    <foreach collection="array" 
             item="id" 
             open="(" 
             separator="," 
             close=")">
        #{id}
    </foreach>
</select>

7.4 集合參數(shù)處理

// Mapper接口
List<User> selectByIds(List<Long> ids);

// XML配置
<select id="selectByIds" resultType="User">
    SELECT * FROM t_user
    WHERE id IN
    <foreach collection="list" 
             item="id" 
             open="(" 
             separator="," 
             close=")">
        #{id}
    </foreach>
</select>

八、最佳實(shí)踐

8.1 參數(shù)命名規(guī)范

// 推薦: 使用@Param注解
User selectByNameAndEmail(
    @Param("userName") String name,
    @Param("userEmail") String email);

// SQL中引用
<select id="selectByNameAndEmail" resultType="User">
    SELECT * FROM t_user
    WHERE user_name = #{userName}
    AND email = #{userEmail}
</select>

8.2 類(lèi)型處理建議

? 基本類(lèi)型優(yōu)先使用包裝類(lèi)避免Null值處理問(wèn)題
? 復(fù)雜類(lèi)型自定義TypeHandler提高代碼可讀性
? 枚舉類(lèi)型使用EnumTypeHandler支持名稱或序號(hào)存儲(chǔ)
? 日期類(lèi)型統(tǒng)一避免類(lèi)型混亂

8.3 性能優(yōu)化建議

? 復(fù)用TypeHandler實(shí)例TypeHandler是線程安全的
? 避免過(guò)度類(lèi)型轉(zhuǎn)換減少不必要的轉(zhuǎn)換開(kāi)銷(xiāo)
? 合理使用JdbcType僅在必要時(shí)指定

8.4 常見(jiàn)問(wèn)題解決

問(wèn)題1: 參數(shù)未綁定

// 問(wèn)題現(xiàn)象
Parameter 'xxx' not found. 
Available parameters are [arg0, arg1, param1, param2]

// 解決方案1: 使用@Param注解
User select(
    @Param("name") String name, 
    @Param("email") String email);

// 解決方案2: 使用默認(rèn)參數(shù)名
User select(String name, String email);
// SQL中使用 #{arg0} #{arg1} 或 #{param1} #{param2}

問(wèn)題2: 類(lèi)型轉(zhuǎn)換異常

// 問(wèn)題現(xiàn)象
Cause: java.lang.NumberFormatException: 
    For input string: "xxx"

// 解決方案: 檢查參數(shù)類(lèi)型映射
<select id="selectById" resultType="User">
    SELECT * FROM t_user 
    WHERE id = #{id, javaType=Long, jdbcType=BIGINT}
</select>

問(wèn)題3: Null值處理

// 問(wèn)題現(xiàn)象
JDBC requires that the JdbcType must be specified 
for all nullable parameters.

// 解決方案1: 指定jdbcType
#{name, jdbcType=VARCHAR}

// 解決方案2: 全局配置
<settings>
    <setting name="jdbcTypeForNull" value="NULL"/>
</settings>

九、總結(jié)

MyBatis的參數(shù)處理模塊是整個(gè)框架的基礎(chǔ)組件,通過(guò)精心設(shè)計(jì)的ParameterHandler和TypeHandler體系,實(shí)現(xiàn)了Java類(lèi)型與JDBC類(lèi)型的無(wú)縫轉(zhuǎn)換。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論

定陶县| 乐山市| 嘉黎县| 宿州市| 孟村| 郁南县| 青冈县| 宜都市| 积石山| 丰都县| 青海省| 长泰县| 常州市| 正蓝旗| 河津市| 体育| 靖宇县| 长春市| 垣曲县| 张掖市| 清苑县| 依安县| 吉安县| 交口县| 博乐市| 拜城县| 巴东县| 德庆县| 利川市| 宁晋县| 巴中市| 河曲县| 邛崃市| 长治县| 九龙坡区| 九江县| 皮山县| 来安县| 盐津县| 兴隆县| 通城县|