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

Hibernate嵌套实体持久化报错Column 'id' cannot be null问题求助

问题根因

你遇到的两个报错核心是双向关联的所有者端未正确设置关联属性,以及级联配置和实体映射不匹配:

  • Exercise和ExerciseSet是双向一对多关联,你在Exercise的@OneToMany上配置了mappedBy = "exercise",说明关联的所有者是ExerciseSet,只有所有者端负责维护外键关系。你现在转换DTO的时候只给Exercise设置了exerciseSets列表,没有给每个ExerciseSet设置对应的exercise属性,Hibernate插入ExerciseSet的时候找不到外键exerciseId的值,才会报Column 'id' cannot be null的错误。
  • 改成CascadeType.MERGE之后ExerciseSet没保存,是因为关联未设置的情况下,Hibernate不会识别到ExerciseSet需要级联持久化,直接忽略了这部分数据。

解决方案

第一步:修正DTO转换逻辑,补全ExerciseSet的关联属性

你担心的循环问题不需要担心,只要在序列化或者toString阶段做对应处理即可,实体类本身设置双向关联是JPA的标准操作。修改你的toWorkout方法里构建ExerciseSet的逻辑:

for (ExerciseSetDto exerciseSetDto : exerciseDto.getExerciseSetDtoList()) {
    ExerciseSet exerciseSet = new ExerciseSet();
    exerciseSet.setSetPosition(exerciseSetDto.getSetPosition());
    exerciseSet.setReps(exerciseSetDto.getReps());
    exerciseSet.setWeight(exerciseSetDto.getWeight());
    // 新增这行,给ExerciseSet设置所属的Exercise
    exerciseSet.setExercise(exercise);
    exerciseSets.add(exerciseSet);
}

第二步:修正Workout和Exercise的关联配置

你现在Workout的@OneToMany用了@JoinColumn,但是Exercise里也有@ManyToOne指向Workout,属于双向关联,最好也给Workout的@OneToMany加上mappedBy,避免重复维护外键:

// 修改Workout类的exercises属性配置
@OneToMany(mappedBy = "workout", cascade = CascadeType.ALL, orphanRemoval = true)
List<Exercise> exercises;

同时在DTO转换的时候也要给每个Exercise设置对应的Workout属性:

Exercise exercise = exerciseMapper.toExercise(exerciseDto);
// 新增这行,给Exercise设置所属的Workout
exercise.setWorkout(workout);
exercise.setExerciseSets(exerciseSets);
exerciseList.add(exercise);

第三步:处理循环引用问题

如果用了@Data注解,默认toString会包含关联属性导致栈溢出,可以给Exercise的workout属性、ExerciseSet的exercise属性加上@ToString.Exclude注解,序列化的时候如果用Jackson,可以加上@JsonBackReference避免序列化循环:

// Exercise类的workout属性
@ManyToOne(fetch = FetchType.LAZY)
@ToString.Exclude
@JsonBackReference
private Workout workout;

// ExerciseSet类的exercise属性
@ManyToOne
@JoinColumn(name="exerciseId", nullable = false)
@ToString.Exclude
@JsonBackReference
private Exercise exercise;

第四步:恢复级联配置为CascadeType.ALL

改完上面的配置之后,把级联改回CascadeType.ALL即可,Workout、Exercise、ExerciseSet三个实体都会正常级联保存。

内容的提问来源于stack exchange,提问作者Mitchel Abrahams

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 21:24:06