Home > Net >  Hibernate nested entity Column 'id' cannot be null
Hibernate nested entity Column 'id' cannot be null

Time:10-19

I'm trying to persist a workout object that has a list of exercises which has a list of exerciseSets. The error i get is when hibernate tries to persist the exerciseSets to the database when i use CascadeType.ALL. When i use MERGE i don't get a error, but the ExerciseSet is not saved to the database. But the Workout and Exercise objects are.

What am i missing here? The only thing i can come up with is that i have to place the exercise object inside the exerciseSet object which kinda ends up in a loop of adding them to each other.

The objects are as follows:

@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Workout {

    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    Long workoutId;

    @Column
    int day;

    @Column
    String workoutName;

    @OneToMany(cascade = CascadeType.ALL,
            orphanRemoval = true)
    @JoinColumn
    List<Exercise> exercises;

    @Column
    String createDate;
}
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Exercise {

    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    private Long exerciseId;

    @ManyToOne(fetch = FetchType.LAZY)
    private Workout workout;

    @OneToMany(mappedBy = "exercise",
            cascade = CascadeType.ALL)
    private List<ExerciseSet> exerciseSets;

    @OneToOne(cascade = CascadeType.MERGE)
    @JoinTable(name = "var_exercise",
            joinColumns =
                    { @JoinColumn(name = "exercise_id")},
            inverseJoinColumns =
                    { @JoinColumn(name = "variation_id" )})
    private ExerciseVariations exerciseVariations;
}
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ExerciseSet {

    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    private Long setId;

    @Column
    private int setPosition;

    @Column
    private int reps;

    @Column
    private int weight;

    @ManyToOne
    @JoinColumn(name="exerciseId", nullable = false)
    private Exercise exercise;
}

I get a WorkoutDto from the frontend and map it to a entity with a mapper as follows

    @PostMapping("/save")
    public void saveWorkout(@RequestBody WorkoutDto workoutDto) {
        workoutService.saveWorkout(workoutMapper.toWorkout(workoutDto));
    }
public Workout toWorkout(WorkoutDto workoutDto) {
        Workout workout = modelMapper.map(workoutDto, Workout.class);

        if (workoutDto.getWorkoutId() != null) {
            workout.setWorkoutId(workout.getWorkoutId());
        }
        workout.setWorkoutName(workoutDto.getWorkoutName());
        workout.setDay(workoutDto.getDay());
        List<Exercise> exerciseList = new ArrayList<>();
        for (ExerciseDto exerciseDto: workoutDto.getExercisesDto()) {
            List<ExerciseSet> exerciseSets = new ArrayList<>();
            for (ExerciseSetDto exerciseSetDto : exerciseDto.getExerciseSetDtoList()) {
                ExerciseSet exerciseSet = new ExerciseSet();
                exerciseSet.setSetPosition(exerciseSetDto.getSetPosition());
                exerciseSet.setReps(exerciseSetDto.getReps());
                exerciseSet.setWeight(exerciseSetDto.getWeight());
                exerciseSets.add(exerciseSet);
            }
            Exercise exercise = exerciseMapper.toExercise(exerciseDto);
            exercise.setExerciseSets(exerciseSets);
            exerciseList.add(exercise);
        }

        workout.setExercises(exerciseList);
        workout.setCreateDate(workoutDto.getCreateDate());

        return workout;
    }

CodePudding user response:

You don't set the Exercise in the ExerciseSet and this is the side that manages the relationship.

You code should look like this:

public Workout toWorkout(WorkoutDto workoutDto) {
    Workout workout = modelMapper.map(workoutDto, Workout.class);

    if (workoutDto.getWorkoutId() != null) {
        workout.setWorkoutId(workout.getWorkoutId());
    }
    workout.setWorkoutName(workoutDto.getWorkoutName());
    workout.setDay(workoutDto.getDay());
    List<Exercise> exerciseList = new ArrayList<>();
    for (ExerciseDto exerciseDto: workoutDto.getExercisesDto()) {
       Exercise exercise = exerciseMapper.toExercise(exerciseDto);

        List<ExerciseSet> exerciseSets = new ArrayList<>();

        for (ExerciseSetDto exerciseSetDto : exerciseDto.getExerciseSetDtoList()) {
            ExerciseSet exerciseSet = new ExerciseSet();
            exerciseSet.setSetPosition(exerciseSetDto.getSetPosition());
            exerciseSet.setReps(exerciseSetDto.getReps());
            exerciseSet.setWeight(exerciseSetDto.getWeight());
            exerciseSet.setExercise(excercise);
            exerciseSets.add(exerciseSet);
        }
        exercise.setExerciseSets(exerciseSets);
        exerciseList.add(exercise);
    }

    workout.setExercises(exerciseList);
    workout.setCreateDate(workoutDto.getCreateDate());

    return workout;
}

And then you can set the CascadeType to ALL.

  • Related