简体   繁体   中英

Updating creates new child object instead of updating the existing one Hibernate

In my spring boot app I have a Workout object with a list of Exercise objects. The problem is when I try to update Workout, the Exercise that belongs to the Workout is not being updated. Instead it's creating new Exercise objects in the database. I've added my Workout Entity and the Exercise Entity Below. Any help would be greatly appreciated.

Workout Class:

@Entity
public class Workout {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;


@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "workout_id")
private List<Exercise> exercises;

public Workout(List<Exercise> exercises) {
    this.exercises = exercises;
}

public Workout() {
}

public Long getId() {
    return id;
}

public void setId(Long id) {
    this.id = id;
}

public List<Exercise> getExercises() {
    return exercises;
}

public void setExercises(List<Exercise> exercises) {
    this.exercises = exercises;
}
}

Exercise Class:

@Entity
@Table
public class Exercise {

@Id
@GeneratedValue
private Long id;


private String exerciseName;
private int weight;
private int actualReps;

@ManyToOne
@JoinColumn(name = "workout_id")
private Workout workout;

public Exercise(){}


public Exercise( String exerciseName, int weight, int actualReps) {
    this.exerciseName = exerciseName;
    this.weight = weight;
    this.actualReps = actualReps;
}

public Long getId() {
    return id;
}

public void setId(Long id) {
    this.id = id;
}

public String getExerciseName() {
    return exerciseName;
}

public void setExerciseName(String exerciseName) {
    this.exerciseName = exerciseName;
}

public int getWeight() {
    return weight;
}

public void setWeight(int weight) {
    this.weight = weight;
}

public int getActualReps() {
    return actualReps;
}

public void setActualReps(int actualReps) {
    this.actualReps = actualReps;
}
}

Controller:

@Controller
public class WorkoutController {

@Autowired
private WorkoutService workoutService;
@Autowired
private OneRepMaxService oneRepMaxService;
@Autowired
private ActualRepsService actualRepsService;
@Autowired
private PercentageService percentageService;
@Autowired
private ExerciseService exerciseService;



@RequestMapping("/")
@SuppressWarnings("unchecked")
public String showWorkout(Long id, Model model,Workout workout){





    Workout firstWorkout = workoutService.findById(Long.valueOf(83));

    model.addAttribute("firstWorkout",firstWorkout);
    model.addAttribute("workout",workout);

    return "workout";
}

@RequestMapping(value="/workoutLogs", method = RequestMethod.POST)
public String saveWorkout(@ModelAttribute Workout workout,Model model) {


    workoutService.saveNewWorkout(workout);


    return "redirect:/workoutLogs";
}


@RequestMapping("/workoutLogs")
public String showAllSavedWorkoutLogs(Model model){


    return "WorkoutLogs";

}



}

WorkoutDao:

@Repository
public class WorkoutDaoImpl implements WorkoutDao {

@Autowired
SessionFactory sessionFactory;

@Override
@SuppressWarnings("unchecked")
public List<Workout> findAll() {
    Session session = sessionFactory.openSession();
    List<Workout> workouts = session.createCriteria(Workout.class).list();
    session.close();
    return workouts;
}

@Override
public Workout getFirstWorkoutExerciseName(Long id) {
    return null;
}

@Override
public Workout getWorkout() {
    return null;


}


@Override
public Workout findById(Long id) {
    Session session = sessionFactory.openSession();
    Workout workout = session.get(Workout.class,id);
    Hibernate.initialize(Workout.class);
    return workout;
}


@Override
public void save(Workout workout) {
    Session session = sessionFactory.openSession();

    // Begin a transaction
    session.beginTransaction();

    // Save the category
    session.saveOrUpdate(workout);

    // Commit the transaction
    session.getTransaction().commit();

    // Close the session
    session.close();
}

@Override
public void delete(Workout workout) {

}


}

Use mappedBy for bidirectional associations and don't use @JoinColumn on each side of the association ( what is @JoinColumn and how it is used in Hibernate ).

@OneToMany(mappedBy = "workout", cascade = CascadeType.ALL)
private List<Exercise> exercises;

@ManyToOne
@JoinColumn(name = "workout_id")
private Workout workout;

Use orphanRemoval = true if you want to Exercise row to be deleted when It doesn't belong to the Workout

@OneToMany(mappedBy = "workout", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Exercise> exercises;

Updating a Workout from a database

  1. Get a Workout from a database.
  2. Update some Exercise from exercises collection.
  3. Update/merge Workout

Updating exercises from a database

You can get a collection of exercises (without Workout ) by Workout id. And update any of them and saveOrUpdate each of them.

Updating a Workout from a request

You should have id in the Workout . Also each Exercise with existing id will be updated (and will created with not existing id , at least for Hibernate 4.11), each Exercise without id will be created, each not existing in the exercises collection Exercise will be deleted with orphanRemoval = true .

Always use merge() for such scenario, saveOrUpdate() will not work (at least exercises will not be deleted).

This is an incorrect method:

@Override
public Workout findById(Long id) {
    Session session = sessionFactory.openSession();
    Workout workout = session.get(Workout.class,id);
    Hibernate.initialize(Workout.class);
    return workout;
}

You need to close a session and this is garbage Hibernate.initialize(Workout.class) .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM