使用MyBatis注解调用Oracle存储过程传递ID数组及TypeHandler使用疑问
解决MyBatis注解调用Oracle存储过程的数组参数问题
我来帮你搞定这个问题——你完全可以继续用TypeHandler传递数组参数,注解版本返回null主要是几个配置细节没和XML版本对齐,咱们一步步梳理:
问题根源分析
你的XML写法能正常运行,是因为:
- 存储过程的OUT CURSOR参数会被自动填充到传入的Map中,后续可以从Map里取出结果;
- 数组参数通过指定的TypeHandler完成了Java List到Oracle ARRAY的转换。
而注解版本返回null,核心是这几个点没处理对:
- 返回值和OUT参数的映射逻辑不匹配:XML用Map接收OUT参数,但注解方法直接返回List,MyBatis无法自动完成转换;
- OUT参数的配置细节:虽然你指定了
resultMap,但参数名称和类型的配置需要更精准; - 数组参数的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指定结果映射规则。
关键注意事项
- 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); }
- ResultMap的有效性:确保
documentData这个ResultMap已经在MyBatis配置中定义,能正确映射CURSOR返回的字段到DocumentData实体的属性; - 参数名称一致性:Map中的key要和注解里
#{}中的参数名完全一致,大小写最好和Oracle存储过程的参数名匹配(Oracle默认不区分,但保持一致能避免意外问题)。
这样调整后,注解版本就能正常传递数组参数并获取存储过程的返回结果了。
内容的提问来源于stack exchange,提问作者tine
相关产品推荐
相关产品推荐

