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

Spring Boot POST请求发送对象数组异常:仅第二个对象传入存储过程

问题:Spring Boot处理POST对象数组时仅最后一个对象被传入存储过程

我在Postman发送的POST请求体是包含两个技能对象的数组:

[
  {
    "skill_name":"cms",
    "skill_desc":"php",
    "is_cerificate_completed":true,
    "emp_skill_id":-1,
    "emp_id":3,
    "status":"SMP",
    "reason_for_reject":"",
    "active":true,
    "submitted":true,
    "user_id":3
  },
  {
    "skill_name":"wordpress",
    "skill_desc":"php",
    "is_cerificate_completed":true,
    "emp_skill_id":-1,
    "emp_id":3,
    "status":"SMP",
    "reason_for_reject":"",
    "active":true,
    "submitted":true,
    "user_id":3
  }
]

但发送后发现只有第二个对象被传入MySQL存储过程执行了。我的Spring Boot服务端处理代码如下:

public void saveEmployeeSkillMatrix(List<EmployeeSkillMatrix> emp_skill_matrix) {
    StoredProcedureQuery save = em.createStoredProcedureQuery("sp_iu_skill_matrix");
    save.registerStoredProcedureParameter("p_emp_skill_id", Integer.class, ParameterMode.IN)
        .registerStoredProcedureParameter("p_emp_id", Integer.class, ParameterMode.IN)
        .registerStoredProcedureParameter("p_skill_name", String.class, ParameterMode.IN)
        .registerStoredProcedureParameter("p_skill_desc", String.class, ParameterMode.IN)
        .registerStoredProcedureParameter("p_is_certificate_completed", Boolean.class, ParameterMode.IN)
        .registerStoredProcedureParameter("p_status", String.class, ParameterMode.IN)
        .registerStoredProcedureParameter("p_reason_for_reject", String.class, ParameterMode.IN)
        .registerStoredProcedureParameter("p_active", Boolean.class, ParameterMode.IN)
        .registerStoredProcedureParameter("p_submitted", Boolean.class, ParameterMode.IN)
        .registerStoredProcedureParameter("p_usr_id", Integer.class, ParameterMode.IN)
        .registerStoredProcedureParameter("error_msg", String.class, ParameterMode.INOUT);

    for(EmployeeSkillMatrix esm : emp_skill_matrix) {
        save.setParameter("p_emp_skill_id", esm.getEmp_skill_id());
        save.setParameter("p_emp_id", esm.getEmp_id());
        save.setParameter("p_skill_name", esm.getSkill_name());
        save.setParameter("p_skill_desc", esm.getSkill_desc());
        save.setParameter("p_is_certificate_completed", esm.isCertificate_completed());
        save.setParameter("p_status", esm.getStatus());
        save.setParameter("p_reason_for_reject", esm.getReason_for_reject());
        save.setParameter("p_active", esm.isActive());
        save.setParameter("p_submitted", esm.isSubmitted());
        save.setParameter("p_usr_id", esm.getUser_id());
        save.setParameter("error_msg", new String("error_msg"));
    }

    String errString = (String) save.getOutputParameterValue("error_msg");
}

想请教下怎么正确接收并处理这个对象数组,让两个对象都能被存储过程处理?


问题分析与解决方法

问题根源

你的代码核心问题有两个:

  1. 没有在循环内执行存储过程:你只是在循环里不断覆盖StoredProcedureQuery的参数值,但从来没调用execute()触发存储过程执行。最后调用getOutputParameterValue()时,会自动触发一次执行,但此时参数已经被最后一个对象覆盖,所以只有第二个对象的参数被执行了。
  2. StoredProcedureQuery的复用问题:这个对象默认是单次执行的,如果你复用它而不重置,参数会被后续的设置覆盖,导致之前的对象参数丢失。

解决方法一:循环内创建新的存储过程实例(推荐)

每次循环都创建新的StoredProcedureQuery,确保每个对象的参数都是独立的,不会互相干扰。修改后的代码如下:

@Transactional // 建议添加事务,确保批量操作的原子性
public void saveEmployeeSkillMatrix(List<EmployeeSkillMatrix> emp_skill_matrix) {
    for(EmployeeSkillMatrix esm : emp_skill_matrix) {
        // 每次循环创建新的存储过程查询实例
        StoredProcedureQuery save = em.createStoredProcedureQuery("sp_iu_skill_matrix");
        // 注册参数(每次都要做,因为是新实例)
        save.registerStoredProcedureParameter("p_emp_skill_id", Integer.class, ParameterMode.IN)
            .registerStoredProcedureParameter("p_emp_id", Integer.class, ParameterMode.IN)
            .registerStoredProcedureParameter("p_skill_name", String.class, ParameterMode.IN)
            .registerStoredProcedureParameter("p_skill_desc", String.class, ParameterMode.IN)
            .registerStoredProcedureParameter("p_is_certificate_completed", Boolean.class, ParameterMode.IN)
            .registerStoredProcedureParameter("p_status", String.class, ParameterMode.IN)
            .registerStoredProcedureParameter("p_reason_for_reject", String.class, ParameterMode.IN)
            .registerStoredProcedureParameter("p_active", Boolean.class, ParameterMode.IN)
            .registerStoredProcedureParameter("p_submitted", Boolean.class, ParameterMode.IN)
            .registerStoredProcedureParameter("p_usr_id", Integer.class, ParameterMode.IN)
            .registerStoredProcedureParameter("error_msg", String.class, ParameterMode.INOUT);

        // 设置当前对象的参数
        save.setParameter("p_emp_skill_id", esm.getEmp_skill_id());
        save.setParameter("p_emp_id", esm.getEmp_id());
        save.setParameter("p_skill_name", esm.getSkill_name());
        save.setParameter("p_skill_desc", esm.getSkill_desc());
        save.setParameter("p_is_certificate_completed", esm.isCertificate_completed());
        save.setParameter("p_status", esm.getStatus());
        save.setParameter("p_reason_for_reject", esm.getReason_for_reject());
        save.setParameter("p_active", esm.isActive());
        save.setParameter("p_submitted", esm.isSubmitted());
        save.setParameter("p_usr_id", esm.getUser_id());
        save.setParameter("error_msg", ""); // 初始化错误信息为空

        // 执行存储过程
        save.execute();

        // 获取并处理当前对象的错误信息
        String errString = (String) save.getOutputParameterValue("error_msg");
        if (errString != null && !errString.isEmpty()) {
            // 这里可以添加错误处理逻辑,比如记录日志、抛出异常等
            throw new RuntimeException("处理技能" + esm.getSkill_name() + "失败:" + errString);
        }
    }
}

解决方法二:复用存储过程实例并重置参数

如果不想每次创建新实例,可以在每次执行后重置参数,但这种方式需要注意清理参数状态:

@Transactional
public void saveEmployeeSkillMatrix(List<EmployeeSkillMatrix> emp_skill_matrix) {
    StoredProcedureQuery save = em.createStoredProcedureQuery("sp_iu_skill_matrix");
    // 一次性注册所有参数
    save.registerStoredProcedureParameter("p_emp_skill_id", Integer.class, ParameterMode.IN)
        .registerStoredProcedureParameter("p_emp_id", Integer.class, ParameterMode.IN)
        .registerStoredProcedureParameter("p_skill_name", String.class, ParameterMode.IN)
        .registerStoredProcedureParameter("p_skill_desc", String.class, ParameterMode.IN)
        .registerStoredProcedureParameter("p_is_certificate_completed", Boolean.class, ParameterMode.IN)
        .registerStoredProcedureParameter("p_status", String.class, ParameterMode.IN)
        .registerStoredProcedureParameter("p_reason_for_reject", String.class, ParameterMode.IN)
        .registerStoredProcedureParameter("p_active", Boolean.class, ParameterMode.IN)
        .registerStoredProcedureParameter("p_submitted", Boolean.class, ParameterMode.IN)
        .registerStoredProcedureParameter("p_usr_id", Integer.class, ParameterMode.IN)
        .registerStoredProcedureParameter("error_msg", String.class, ParameterMode.INOUT);

    for(EmployeeSkillMatrix esm : emp_skill_matrix) {
        // 设置当前对象的参数
        save.setParameter("p_emp_skill_id", esm.getEmp_skill_id());
        save.setParameter("p_emp_id", esm.getEmp_id());
        save.setParameter("p_skill_name", esm.getSkill_name());
        save.setParameter("p_skill_desc", esm.getSkill_desc());
        save.setParameter("p_is_certificate_completed", esm.isCertificate_completed());
        save.setParameter("p_status", esm.getStatus());
        save.setParameter("p_reason_for_reject", esm.getReason_for_reject());
        save.setParameter("p_active", esm.isActive());
        save.setParameter("p_submitted", esm.isSubmitted());
        save.setParameter("p_usr_id", esm.getUser_id());
        save.setParameter("error_msg", "");

        // 执行存储过程
        save.execute();

        // 处理错误信息
        String errString = (String) save.getOutputParameterValue("error_msg");
        if (errString != null && !errString.isEmpty()) {
            throw new RuntimeException("处理技能" + esm.getSkill_name() + "失败:" + errString);
        }

        // 重置参数,以便下一次循环使用
        save.unwrap(javax.persistence.Query.class).clearParameters();
    }
}

额外注意事项

  • 你的请求体里字段is_cerificate_completed拼写有误(少了一个t,正确应该是is_certificate_completed),要确保EmployeeSkillMatrix类的字段映射和请求体一致,避免参数值为null的情况。
  • 建议给方法添加@Transactional注解,确保批量操作的原子性——要么所有对象都处理成功,要么全部回滚,避免部分成功部分失败的情况。
  • 错误信息初始化不要用new String("error_msg"),直接传空字符串更合理,因为存储过程会返回实际的错误内容。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:19:40