You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MyBatis自定义EncryptedStringTypeHandler每个字段被重复调用两次问题咨询

问题原因

MyBatis在处理嵌套<collection>的普通result映射时,会执行两次类型处理器:第一次是处理结果集行、判断父子关联关系时的预读取,第二次是给子对象属性赋值时的正式读取,因此会出现两次调用的情况。而constructor构造注入逻辑会直接完成映射后一次性实例化对象,不会触发二次调用。


解决方案

方案1:使用constructor映射(已验证可行,优先推荐)

直接将子对象的映射改为构造器注入方式,示例配置如下:

<resultMap id="WorkspacePackageDTOMap" type="com.example.dto.WorkspacePackageDTO" autoMapping="true">
  <id column="uid" property="uid" />
  <collection property="participants" ofType="com.example.dto.ParticipantDTO">
    <constructor>
      <idArg column="part_uid" javaType="String" name="uid"/>
      <arg column="given_name" javaType="String" name="givenName" typeHandler="com.example.dao.EncryptedStringTypeHandler"/>
      <arg column="middle_name" javaType="String" name="middleName" typeHandler="com.example.dao.EncryptedStringTypeHandler"/>
      <arg column="family_name" javaType="String" name="familyName" typeHandler="com.example.dao.EncryptedStringTypeHandler"/>
      <arg column="generation_qualifier" javaType="String" name="generationQualifier"/>
      <arg column="type" javaType="String" name="type"/>
    </constructor>
  </collection>
</resultMap>

注意要确保ParticipantDTO存在对应参数名、参数类型的构造方法即可。


方案2:升级MyBatis版本

该重复调用类型处理器的问题是MyBatis低版本的已知缺陷,升级到3.5.10及以上版本即可直接修复,无需修改现有配置。


方案3:给自定义类型处理器增加本地缓存

如果不能调整配置也不能升级版本,可以在EncryptedStringTypeHandler的getResult方法中增加临时缓存,避免重复执行加密/解密逻辑:

public class EncryptedStringTypeHandler extends BaseTypeHandler<String> {
    // 用ThreadLocal缓存当前结果集已处理过的列值,避免重复计算
    private final ThreadLocal<Map<String, String>> resultCache = ThreadLocal.withInitial(HashMap::new);

    @Override
    public String getResult(ResultSet rs, String columnName) throws SQLException {
        String key = rs.getStatement().hashCode() + ":" + columnName + ":" + rs.getRow();
        if (resultCache.get().containsKey(key)) {
            String value = resultCache.get().get(key);
            // 读取完当前行后清空缓存避免内存泄漏
            if (rs.isLast()) {
                resultCache.remove();
            }
            return value;
        }
        String rawValue = rs.getString(columnName);
        String decryptedValue = decrypt(rawValue); // 你的原有解密逻辑
        resultCache.get().put(key, decryptedValue);
        if (rs.isLast()) {
            resultCache.remove();
        }
        return decryptedValue;
    }
    // 其余方法保持原有逻辑即可
}

内容的提问来源于stack exchange,提问作者SURAJ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 06:18:01