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"); }
想请教下怎么正确接收并处理这个对象数组,让两个对象都能被存储过程处理?
问题分析与解决方法
问题根源
你的代码核心问题有两个:
- 没有在循环内执行存储过程:你只是在循环里不断覆盖
StoredProcedureQuery的参数值,但从来没调用execute()触发存储过程执行。最后调用getOutputParameterValue()时,会自动触发一次执行,但此时参数已经被最后一个对象覆盖,所以只有第二个对象的参数被执行了。 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
相关产品推荐
相关产品推荐

