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

使用MyBatis注解调用Oracle存储过程传递ID数组及TypeHandler使用疑问

解决MyBatis注解调用Oracle存储过程的数组参数问题

我来帮你搞定这个问题——你完全可以继续用TypeHandler传递数组参数,注解版本返回null主要是几个配置细节没和XML版本对齐,咱们一步步梳理:

问题根源分析

你的XML写法能正常运行,是因为:

  • 存储过程的OUT CURSOR参数会被自动填充到传入的Map中,后续可以从Map里取出结果;
  • 数组参数通过指定的TypeHandler完成了Java List到Oracle ARRAY的转换。

而注解版本返回null,核心是这几个点没处理对:

  1. 返回值和OUT参数的映射逻辑不匹配:XML用Map接收OUT参数,但注解方法直接返回List,MyBatis无法自动完成转换;
  2. OUT参数的配置细节:虽然你指定了resultMap,但参数名称和类型的配置需要更精准;
  3. 数组参数的jdbcType需要明确对应Oracle的ARRAY类型。

修正后的解决方案

方案1:和XML逻辑保持一致,用Map接收OUT参数

这种方式最稳妥,完全复刻XML的运行逻辑:

@Mapper
public interface PackageMapper {
    @Select(value = "{call PKG.GET_ORGANISATION_DOCUMENT (" +
            "#{ORGA_ID,javaType=java.lang.Long,jdbcType=NUMERIC,mode=IN}," +
            "#{PERSON_ID,javaType=java.lang.Long,jdbcType=NUMERIC,mode=IN}," +
            "#{GROUP_IDS,javaType=java.util.List,jdbcType=ARRAY,mode=IN,typeHandler=de.xyz.handlers.ArrayOfNumberTypeHandler}," +
            "#{TEMPLATE_IDS,javaType=java.util.List,jdbcType=ARRAY,mode=IN,typeHandler=de.xyz.handlers.ArrayOfNumberTypeHandler}," +
            "#{VALIDFROM,javaType=java.util.Date,jdbcType=TIMESTAMP,mode=IN}," +
            "#{VALIDUNTIL,javaType=java.util.Date,jdbcType=TIMESTAMP,mode=IN}," +
            "#{OUT,javaType=java.sql.ResultSet,jdbcType=CURSOR,mode=OUT,resultMap=documentData}" +
            ") }")
    @Options(statementType = StatementType.CALLABLE)
    void getDocument(Map<String, Object> params);
}

调用时从Map中取出结果:

Map<String, Object> params = new HashMap<>();
params.put("ORGA_ID", 123L);
params.put("PERSON_ID", 456L);
params.put("GROUP_IDS", Arrays.asList(1,2,3));
params.put("TEMPLATE_IDS", Arrays.asList(4,5,6));
params.put("VALIDFROM", new Date());
params.put("VALIDUNTIL", new Date());

packageMapper.getDocument(params);
// 从Map中获取存储过程返回的结果
List<DocumentData> documents = (List<DocumentData>) params.get("OUT");

方案2:直接返回List(简化调用逻辑)

如果你希望方法直接返回List,需要调整OUT参数的配置并关联ResultMap:

@Mapper
public interface PackageMapper {
    @Select(value = "{#{result,javaType=java.util.List,jdbcType=CURSOR,mode=OUT,resultMap=documentData} = call PKG.GET_ORGANISATION_DOCUMENT (" +
            "#{ORGA_ID,javaType=java.lang.Long,jdbcType=NUMERIC,mode=IN}," +
            "#{PERSON_ID,javaType=java.lang.Long,jdbcType=NUMERIC,mode=IN}," +
            "#{GROUP_IDS,javaType=java.util.List,jdbcType=ARRAY,mode=IN,typeHandler=de.xyz.handlers.ArrayOfNumberTypeHandler}," +
            "#{TEMPLATE_IDS,javaType=java.util.List,jdbcType=ARRAY,mode=IN,typeHandler=de.xyz.handlers.ArrayOfNumberTypeHandler}," +
            "#{VALIDFROM,javaType=java.util.Date,jdbcType=TIMESTAMP,mode=IN}," +
            "#{VALIDUNTIL,javaType=java.util.Date,jdbcType=TIMESTAMP,mode=IN}" +
            ") }")
    @Options(statementType = StatementType.CALLABLE)
    @ResultMap("documentData")
    List<DocumentData> getDocument(Map<String, Object> params);
}

这里的关键是把OUT参数命名为result,让MyBatis自动关联到方法的返回值,同时通过@ResultMap指定结果映射规则。

关键注意事项

  1. TypeHandler的正确性:你的ArrayOfNumberTypeHandler要确保能正确转换Java List到Oracle ARRAY,比如setParameter方法要这样实现:
@Override
public void setParameter(PreparedStatement ps, int i, List<Long> parameter, JdbcType jdbcType) throws SQLException {
    Connection conn = ps.getConnection();
    // 这里的"NUMBER"要和Oracle存储过程中定义的数组类型一致,如果是自定义类型要替换成对应的名称
    Array array = conn.createArrayOf("NUMBER", parameter.toArray());
    ps.setArray(i, array);
}
  1. ResultMap的有效性:确保documentData这个ResultMap已经在MyBatis配置中定义,能正确映射CURSOR返回的字段到DocumentData实体的属性;
  2. 参数名称一致性:Map中的key要和注解里#{}中的参数名完全一致,大小写最好和Oracle存储过程的参数名匹配(Oracle默认不区分,但保持一致能避免意外问题)。

这样调整后,注解版本就能正常传递数组参数并获取存储过程的返回结果了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 14:02:30