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

Spring Data JPA调用多out参数存储过程报Type cannot be null异常

问题现象

现有MySQL存储过程getProjectParams,接收1个int类型入参projectId,定义了companyName、projectNumber、projectOwner三个OUT参数,用于根据项目ID查询对应项目的公司名称、项目编号、项目负责人信息。
基于Spring Data JPA开发时,Repository层直接使用@Procedure注解定义了返回值为Map<String, Object>的getProjectParams(long projectId)方法,Service层调用该方法时抛出如下异常,无法正常获取三个输出参数:

Type cannot be null; nested exception is java.lang.IllegalArgumentException: Type cannot be null

报错根因

Spring Data JPA 不会自动识别多OUT参数存储过程的参数类型,也不支持直接将多OUT返回值自动映射为Map<String, Object>:如果没有显式声明所有OUT参数的名称、类型、参数模式,框架在处理返回值时找不到对应的类型映射规则,就会抛出类型为空的非法参数异常。

可落地的解决实现

提供两种稳定可用的实现方式,按需选择即可:

  • 方式一:基于@NamedStoredProcedureQuery + 接口投影实现(适配Repository层直接调用的写法)
    1. 首先在项目对应的Project实体类上,显式声明存储过程的所有入参、出参配置,明确每个参数的模式、名称、类型:
import jakarta.persistence.*;

@Entity
@NamedStoredProcedureQuery(
        name = "getProjectParams",
        procedureName = "getProjectParams",
        parameters = {
                @StoredProcedureParameter(mode = ParameterMode.IN, name = "projectId", type = Integer.class),
                @StoredProcedureParameter(mode = ParameterMode.OUT, name = "companyName", type = String.class),
                @StoredProcedureParameter(mode = ParameterMode.OUT, name = "projectNumber", type = String.class),
                @StoredProcedureParameter(mode = ParameterMode.OUT, name = "projectOwner", type = String.class)
        }
)
public class Project {
    // 实体类自身的字段、方法省略
}
  1. 定义接口投影用来接收OUT参数返回值,接口内的get方法名必须和存储过程OUT参数名严格一致:
public interface ProjectParamResult {
    String getCompanyName();
    String getProjectNumber();
    String getProjectOwner();
}
  1. 调整Repository层的方法定义,绑定存储过程配置,返回值用上面定义的投影接口,入参加上@Param注解绑定参数名:
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.query.Procedure;
import org.springframework.data.repository.query.Param;

public interface ProjectRepository extends JpaRepository<Project, Long> {
    @Procedure(name = "getProjectParams")
    ProjectParamResult getProjectParams(@Param("projectId") long projectId);
}

后续Service层直接调用该Repository方法,就能从返回的ProjectParamResult对象中拿到三个OUT参数的值。

  • 方式二:基于EntityManager原生调用存储过程(无需配置实体注解,灵活度更高)
    如果不想在实体类上耦合存储过程配置,可以直接在Service层注入EntityManager,手动注册参数后调用,结果可直接封装为Map返回:
import jakarta.persistence.EntityManager;
import jakarta.persistence.ParameterMode;
import jakarta.persistence.StoredProcedureQuery;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;

@Service
public class ProjectService {
    private final EntityManager entityManager;

    public ProjectService(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    public Map<String, Object> getProjectParams(long projectId) {
        StoredProcedureQuery spQuery = entityManager.createStoredProcedureQuery("getProjectParams");
        // 注册入参
        spQuery.registerStoredProcedureParameter("projectId", Integer.class, ParameterMode.IN);
        // 注册三个OUT参数,必须显式指定类型
        spQuery.registerStoredProcedureParameter("companyName", String.class, ParameterMode.OUT);
        spQuery.registerStoredProcedureParameter("projectNumber", String.class, ParameterMode.OUT);
        spQuery.registerStoredProcedureParameter("projectOwner", String.class, ParameterMode.OUT);
        // 设置入参值
        spQuery.setParameter("projectId", (int) projectId);
        // 执行存储过程
        spQuery.execute();

        // 提取OUT参数封装为Map
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("companyName", spQuery.getOutputParameterValue("companyName"));
        resultMap.put("projectNumber", spQuery.getOutputParameterValue("projectNumber"));
        resultMap.put("projectOwner", spQuery.getOutputParameterValue("projectOwner"));
        return resultMap;
    }
}
注意事项
  • 所有参数(包含IN、OUT)的名称必须和MySQL存储过程中定义的参数名完全一致,避免参数绑定失败。
  • 参数的Java类型要和MySQL中定义的字段类型匹配,比如MySQL的varchar/char类型对应Java的String,int类型对应Java的Integer,避免类型转换异常。
  • 不要直接给@Procedure标注的Repository方法设置Map<String, Object>作为返回值,Spring Data JPA默认没有提供对应类型的自动映射转换器,会触发类型识别失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 16:03:32