调用CrudRepository.save()报JPASystemException:需手动分配实体ID问题
repository.save()保存MeasurementsFile失败,已设置ID仍报错 问题详情
尝试将MeasurementsFile对象保存至仓库时,repository.save(measurementsFile)完全无效。此前解析器返回空List<MeasurementRow>时代码可正常运行,现在却出现异常。测试时硬编码ID为0L,上传接口返回“could not upload...”提示,且程序在打印“inserted measurementRows”日志后停止,即使移除.setId()和.setMeasurementsMic1()操作,问题依然存在。
相关代码
控制器上传接口
@PostMapping("/upload") public ResponseEntity<ResponseMessage> uploadMeasurements(@RequestParam("file") MultipartFile file) { String message = ""; logger.info("POST: excel"); if (ExcelHelper.hasExcelFormat(file)) { try { logger.info("file name: "+file.getOriginalFilename()); fileService.saveMeasurementsFile(file,0L); message = "Uploaded the file successfully: " + file.getOriginalFilename(); return ResponseEntity.status(HttpStatus.OK).body(new ResponseMessage(message)); } catch (Exception e) { message = "Could not upload the file: " + file.getOriginalFilename() + "!"; return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(new ResponseMessage(message)); } } message = "Please upload an excel file!"; return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseMessage(message)); }
文件服务的saveMeasurementsFile方法
public void saveMeasurementsFile(MultipartFile file, long id) { try { logger.info("reached before excelhelper is called, input stream:"+file.getInputStream()+" with name "+ file.getOriginalFilename() +" and contenttype: "+ file.getContentType()); List<MeasurementRow> measurementRows = ExcelHelper.toMeasurementRows(file.getInputStream()); logger.info(""+measurementRows.size()+" rows"); MeasurementsFile measurementsFile = new MeasurementsFile(); logger.info("created new MeasurementsFile"); measurementsFile.setMeasurementsMic1(measurementRows); logger.info("inserted measurementRows"); measurementsFile.setId(id); logger.info("set id: "+id); measurementRepository.save(measurementsFile); logger.info("saved MeasurementsFile"); } catch (IOException e) { throw new RuntimeException("fail to store excel data!"); } }
MeasurementsFile实体类
@Entity public class MeasurementsFile { @Id private Long id; @Column @OneToMany private List<MeasurementRow> measurementsMic1; @Column @OneToMany private List<MeasurementRow> measurementsMic2;//当配置有两个麦克风时,需上传第二个输入文件 @Column private double height; @Column private int[] referenceSpeeds; //乘用车和重型车辆的参考速度 @Column private boolean hasSecondaryMic; @Column private boolean hasBackingboard; //列ID及其默认值 private int numberColumnID=0; private int dateColumnID=0; private int timeColumnID=0; private int vehicleColumnID=0; private int speedColumnID=0; private int airTempColumnID=0; private int roadTempColumnID=0;//(可选) private int windSpeedColumnID=0;//(可选) private int laMaxColumnID=0; private int laMaxArrayColumnID=0;//默认26个元素(可选) }
(已省略自动生成的导入、Getter和Setter方法)
错误信息
打印异常栈信息得到:
org.springframework.orm.jpa.JpaSystemException: ids for this class must be manually assigned before calling save(): be.uantwerpen.ft.se.tutorial_backend.model.users.MeasurementsFile
明明已经调用.setId(id),为何仍报此错误?
解决方案
1. 修正关联字段的注解错误
@Column注解不能用于@OneToMany关联字段,这是错误用法,需直接移除关联字段上的@Column。同时补充级联配置,确保保存MeasurementsFile时能自动处理关联的MeasurementRow:
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) private List<MeasurementRow> measurementsMic1; @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) private List<MeasurementRow> measurementsMic2;
2. 检查MeasurementRow的ID配置
报错的核心原因并非MeasurementsFile的ID,而是关联的MeasurementRow实体未正确设置ID生成策略。当保存MeasurementsFile时,JPA会级联保存关联的MeasurementRow列表,若MeasurementRow的ID需要手动分配但未设置,就会触发该错误。
如果MeasurementRow的ID是自增类型,需在其@Id字段添加生成策略注解:
@Entity public class MeasurementRow { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) // 或根据数据库选择AUTO等策略 private Long id; // 其他字段... }
3. 验证MeasurementsFile的ID有效性
测试时使用的ID为0L,部分数据库不允许0作为合法主键值。可以尝试使用非0的合法ID,或者将MeasurementsFile的ID改为自动生成策略,无需手动设置:
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
内容的提问来源于stack exchange,提问作者hendrikvalcke

