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

Spring Boot集成MyBatis调用Oracle存储过程返回Null问题排查

Spring Boot + MyBatis 调用Oracle存储过程返回null问题

问题描述

在Spring Boot项目中使用MyBatis调用Oracle存储过程时遇到问题:存储过程proc_test在SQL Developer中测试正常,但通过应用调用时返回null。

存储过程代码

CREATE OR REPLACE PROCEDURE proc_test (
    p_id IN VARCHAR2,
    p_output OUT VARCHAR2
) AS
BEGIN
    -- Sample logic: returning a static string for simplicity
    p_output := 'Sample TEST';
END;
/

MyBatis Mapper配置

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.mapping.StatementType;

import java.util.Map;

@Mapper
public interface ProcedureMapper {

    @Select("{ CALL proc_test(#{id, mode=IN, jdbcType=VARCHAR}, #{output, mode=OUT, jdbcType=VARCHAR}) }")
    @Options(statementType = StatementType.CALLABLE)
    void callProcedure(@Param("id") String id, @Param("output") Map<String, Object> output);
}

服务层代码

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class ProcedureService {

    @Autowired
    private ProcedureMapper procedureMapper;

    public String callProcedure(String id) {
        Map<String, Object> resultMap = new HashMap<>();
        procedureMapper.callProcedure(id, resultMap);
        return (String) resultMap.get("output");
    }
}

控制器代码

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ProcedureController {

    @Autowired
    private ProcedureService procedureService;

    @GetMapping("/callProcedure")
    public String callProcedure(@RequestParam String id) {
        return procedureService.callProcedure(id);
    }
}

已尝试的操作

  • 确认输入参数id已从API正确传递
  • 验证存储过程在SQL Developer中运行正常
  • 检查MyBatis与Spring Boot配置
  • 添加日志确认id已正确传递给存储过程

问题

  1. 为何存储过程单独运行正常,但Result Map为空且API返回null?
  2. 是否遗漏了MyBatis或Spring Boot的配置步骤?
  3. MyBatis处理Oracle存储过程OUT参数是否有已知问题?

问题原因与解决方案

1. 核心问题:OUT参数的Map绑定方式错误

当前Mapper方法中,显式传递Map<String, Object>作为OUT参数载体的写法,无法让MyBatis正确将存储过程的输出值注入到Map中。MyBatis处理CALLABLE语句的OUT参数时,需要调整参数绑定逻辑。

2. 修正方案一:让MyBatis自动返回包含OUT参数的Map

修改Mapper接口方法,无需手动传递Map,改为让MyBatis直接返回存储过程的输出参数映射:

@Mapper
public interface ProcedureMapper {

    @Select("{ CALL proc_test(#{id, mode=IN, jdbcType=VARCHAR}, #{output, mode=OUT, jdbcType=VARCHAR}) }")
    @Options(statementType = StatementType.CALLABLE)
    Map<String, Object> callProcedure(@Param("id") String id);
}

对应服务层代码修改为:

public String callProcedure(String id) {
    Map<String, Object> resultMap = procedureMapper.callProcedure(id);
    return (String) resultMap.get("output");
}

这种方式下,MyBatis会自动将OUT参数output存入返回的Map中。

3. 修正方案二:匹配存储过程参数名

如果坚持手动传递Map,需要确保Mapper中的参数名与存储过程的输出参数名完全一致。存储过程的输出参数是p_output,调整Mapper和服务层代码:

@Mapper
public interface ProcedureMapper {

    @Select("{ CALL proc_test(#{id, mode=IN, jdbcType=VARCHAR}, #{p_output, mode=OUT, jdbcType=VARCHAR}) }")
    @Options(statementType = StatementType.CALLABLE)
    void callProcedure(@Param("id") String id, @Param("p_output") Map<String, Object> output);
}

服务层获取值时改为:

return (String) resultMap.get("p_output");

4. 其他配置检查

  • 确认Oracle JDBC驱动版本与MyBatis、Spring Boot版本兼容,避免版本冲突导致参数绑定失败;
  • 开启MyBatis DEBUG日志,查看执行的SQL语句和参数绑定细节,确认OUT参数是否被正确处理;
  • 检查MyBatis配置中mapUnderscoreToCamelCase的设置(如果需要下划线转驼峰,但当前场景参数名无下划线,可忽略)。

5. MyBatis处理Oracle存储过程OUT参数的常见问题

MyBatis对Oracle存储过程的OUT参数支持成熟,常见问题多来自:

  • 参数名称不匹配(Mapper中的参数名与存储过程的参数名不一致);
  • JDBC类型指定错误(比如将VARCHAR2错误指定为VARCHAR,部分版本会出现兼容问题);
  • 语句类型未设置为StatementType.CALLABLE(你已正确设置,此点可排除);
  • 参数的mode属性设置错误(IN/OUT/INOUT混淆)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 12:43:14