SpringBoot中如何修改PUT方法实现数组批量更新?
批量更新实现方案
问题背景
现有SpringBoot服务支持单ID单条记录更新,代码如下:
Controller代码
@PutMapping("/{cveEstado}/{comentario}/{idControl}") private void actualizaDatos(@PathVariable("cveEstado") String cveEstado, @PathVariable("comentario") String comentario, @PathVariable("idControl") String idControl){ controlService.actualizar(cveEstado, comentario, idControl); }
Service及Repository代码
// Service实现 @Override public void actualizar(String cveEstado, String comentario, String idControl) { repository.actualizarCampos(cveEstado, comentario, idControl); } // Repository方法 @Transactional @Modifying @Query(value = "UPDATE CONTROL C SET CVEESTADO=?1, COMENTARIO=?2 WHERE C.IDCONTROL=?3", nativeQuery = true) void actualizarCampos(String cveEstado, String comentario, String idControl);
需求:修改为接收数组批量更新多条不同ID的记录,支持更新全部或部分字段。
具体实现步骤
1. 定义批量更新DTO
创建DTO封装单条记录的更新信息,支持部分字段为空:
public class ControlUpdateDTO { private String idControl; // 必传,记录ID private String cveEstado; // 可选,需要更新的状态 private String comentario; // 可选,需要更新的备注 // 构造方法、getter、setter public ControlUpdateDTO() {} public ControlUpdateDTO(String idControl, String cveEstado, String comentario) { this.idControl = idControl; this.cveEstado = cveEstado; this.comentario = comentario; } // 生成getter和setter方法 }
2. 修改Controller接收批量请求
将路径参数改为请求体接收DTO数组,同时修正Controller方法权限为public(Spring无法映射私有方法):
@PutMapping("/batch") public void actualizaDatosBatch(@RequestBody List<ControlUpdateDTO> updateList) { controlService.actualizarBatch(updateList); }
3. 更新Service层逻辑
新增批量更新方法,可选择两种实现方式:
@Override @Transactional public void actualizarBatch(List<ControlUpdateDTO> updateList) { // 方式一:循环调用单条更新(简单易维护,适合小批量数据) // updateList.forEach(dto -> { // if (dto.getCveEstado() != null || dto.getComentario() != null) { // repository.actualizarCampos(dto.getCveEstado(), dto.getComentario(), dto.getIdControl()); // } // }); // 方式二:批量SQL更新(性能更优,适合大批量数据) repository.actualizarCamposBatch(updateList); }
4. 实现Repository批量更新
方案A:原生SQL批量更新(单SQL语句完成)
通过CASE WHEN拼接批量更新逻辑,减少数据库交互次数:
@Transactional @Modifying @Query(value = """ UPDATE CONTROL C SET CVEESTADO = CASE <foreach collection="list" item="dto" separator=" "> WHEN C.IDCONTROL = :#{dto.idControl} THEN :#{dto.cveEstado} </foreach> ELSE CVEESTADO END, COMENTARIO = CASE <foreach collection="list" item="dto" separator=" "> WHEN C.IDCONTROL = :#{dto.idControl} THEN :#{dto.comentario} </foreach> ELSE COMENTARIO END WHERE C.IDCONTROL IN (:ids) """, nativeQuery = true) void actualizarCamposBatch(@Param("list") List<ControlUpdateDTO> updateList, @Param("ids") List<String> ids);
注:若使用Spring Data JPA原生SQL无法解析
<foreach>,可通过EntityManager动态构建SQL,或改用MyBatis实现更灵活的批量SQL拼接。
方案B:JPA实体批量更新(更贴合Spring Data风格)
先查询对应实体,修改非空字段后批量保存:
// 在Repository中添加查询方法 List<Control> findAllByIdIn(List<String> ids); // 在Service中实现批量更新逻辑 @Override @Transactional public void actualizarBatch(List<ControlUpdateDTO> updateList) { List<String> ids = updateList.stream().map(ControlUpdateDTO::getIdControl).collect(Collectors.toList()); List<Control> controls = repository.findAllByIdIn(ids); controls.forEach(control -> { updateList.stream() .filter(dto -> dto.getIdControl().equals(control.getIdControl())) .findFirst() .ifPresent(dto -> { // 仅更新非空字段,实现部分更新 if (dto.getCveEstado() != null) { control.setCveEstado(dto.getCveEstado()); } if (dto.getComentario() != null) { control.setComentario(dto.getComentario()); } }); }); repository.saveAll(controls); }
5. 前端请求示例(Angular)
构造DTO数组发送PUT请求:
interface ControlUpdateDTO { idControl: string; cveEstado?: string; comentario?: string; } // 批量更新请求方法 updateBatch(updates: ControlUpdateDTO[]): Observable<void> { return this.http.put<void>('/api/control/batch', updates); }
内容的提问来源于stack exchange,提问作者Root93
相关产品推荐
相关产品推荐

