I think (hopefully) I'm doing all the right things. Googled it a lot, couldn't find what's wrong.
Profile
can have multiple Appointment
s.
Each Appointment
has a Department
.
Profile:
@Entity
@Table(name = "PROFILE")
public class Profile {
/**
*
*/
private Set<Appointment> appointments = new HashSet<Appointment>();
/**
* @return the appointments
*/
@OneToMany(mappedBy = "profile", cascade = { CascadeType.PERSIST,
CascadeType.MERGE })
public Set<Appointment> getAppointments() {
return appointments;
}
/**
* @param appointments
* the appointments to set
*/
public void setAppointments(Set<Appointment> appointments) {
this.appointments = appointments;
}
/**
*
* @param apt
*/
public void addAppointment(Appointment apt) {
apt.setProfile(this);
appointments.add(apt);
}
}
Appointment:
@Entity
@Table(name = "APPOINTMENT")
public class Appointment {
/**
*
*/
private Department department;
private Profile profile;
/**
* @return the department
*/
@ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE }, fetch = FetchType.LAZY)
@JoinColumn(name = "DEPARTMENT_ID")
@Index(name = "IDX_APPOINTMENT_DEPARTMENT")
@ForeignKey(name = "FK_APPOINTMENT_DEPARTMENT")
public Department getDepartment() {
return department;
}
/**
* @param department
* the department to set
*/
public void setDepartment(Department department) {
this.department = department;
}
/**
* @return the profile
*/
@ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE }, fetch = FetchType.LAZY)
@JoinColumn(name = "PROFILE_ID")
@Index(name = "IDX_APPOINTMENT_PROFILE")
@ForeignKey(name = "FK_APPOINTMENT_PROFILE")
public Profile getProfile() {
return profile;
}
/**
* @param profile
* the profile to set
*/
public void setProfile(Profile profile) {
this.profile = profile;
}
}
Department:
@Entity
@Table(name = "DEPARTMENT")
public class Department {
/**
*
*/
private Set<Appointment> appointments = new HashSet<Appointment>();
private Set<Department> children = new HashSet<Department>();
private Department parent;
/**
* @return the appointments
*/
@OneToMany(mappedBy = "department")
public Set<Appointment> getAppointments() {
return appointments;
}
/**
* @param appointments
* the appointments to set
*/
public void setAppointments(Set<Appointment> appointments) {
this.appointments = appointments;
}
/**
* @return the children
*/
@OneToMany(mappedBy = "parent", cascade = { CascadeType.PERSIST,
CascadeType.MERGE })
public Set<Department> getChildren() {
return children;
}
/**
* @param children
* the children to set
*/
public void setChildren(Set<Department> children) {
this.children = children;
}
/**
* @return the parent
*/
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "PARENTDEPARTMENT_ID")
@Index(name = "IDX_DEPARTMENT_CHILDREN")
@ForeignKey(name = "FK_DEPARTMENT_CHILDREN")
public Department getParent() {
return parent;
}
/**
* @param parent
* the parent to set
*/
public void setParent(Department parent) {
this.parent = parent;
}
}
The errant @Service
code:
@Transactional(propagation = Propagation.REQUIRED)
private Profile addNewProfile(ProfileJxb profileJxb) {
logger.entry();
Profile profile = new Profile(profileJxb);
for (AppointmentJxb aptJxb : profileJxb.getAppointments()
.getAppointmentJxb()) {
Appointment apt = new Appointment(aptJxb);
Department d = getDepartmentByName(aptJxb.getDepartment()); // previously fetched
apt.setDepartment(d);
// d.getAppointments().add(apt); // another failed attempt, this results in LazyInitException
profile.addAppointment(apt);
}
profile = profileDao.makePersistent(profile); // Exception thrown here!
logger.exit();
return profile;
}
I hope I'm missing something, as I'm fetching and setting Department
in a persistent state to avoid this exception.
Thank you.
When you need to save a new entity that references detached entities via cascaded relationships you have to use merge()
instead of persist()
, but it's not the best way to solve this problem.
The real problem is that department
relaionship has cascading at all. Cascading makes sense when referring to entities that the entity in question logically own. But in your case you have a predefined set of Department
s that is managed separately. Therefore you never need to cascade any operation from Appointment
to Department
.
So, the best solution is to remove cascading from department
relationship.