Imagine the following models:
Employee:
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "employee_project", joinColumns = @JoinColumn(name = "Emp_Id"), inverseJoinColumns = @JoinColumn(name = "Proj_id"))
private Set<Project> projects = new HashSet<Project>();
Project:
@ManyToMany(mappedBy = "projects")
private Set<Employee> employees = new HashSet<Employee>();
Now if I create a new employee that refers to an existing project and try to persist that employee, I get an error:
detached entity passed to persist: Project
I create the employee as follows:
public void createNewEmployee(EmployeeDTO empDTO) {
Employee emp = new Employee();
// add stuff from DTO, including projects
repository.saveAndFlush(emp); // FAILS
}
and I update existing ones like this:
public void updateEmployee(EmployeeDTO empDTO) {
Employee emp = repository.findOne(empDTO.getId());
// set stuff from DTO, including projects
repository.saveAndFlush(emp); // WORKS!
}
I guess you're interacting with the repository without expanding the transaction boundaries appropriately. By default, the transaction (and thus session) boundary is at the repository method level. This causes the Project
instance to be detached from the EntityManager
, so that it cannot be included in a persist operation.
The solution here is to extend the transaction boundary to the client:
@Component
class YourRepositoryClient {
private final ProjectRepository projects;
private final EmployeeRepository employees;
// … constructor for autowiring
@Transactional
public void doSomething() {
Project project = projects.findOne(1L);
Employee employee = employees.save(new Employee(project));
}
}
This approach causes the Project
instance stay a managed entity and thus the persist operation to be executed for the fresh Employee
instance being handled correctly.
The difference with the two repository interactions is that in the second case you'll have a detached instance (has already been persisted, has an id set), where as in the first example you have a completely unmanaged instances that does not have an id set. The id property is what causes the repository to differentiate between calling persist(…)
and merge(…)
. So the first approach will cause a persist(…)
to be triggered, the second will cause a merge(…)
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With