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
Workout
from a database. Exercise
from exercises
collection. 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.