How to save a new entity that refers existing entity in Spring JPA?
java, jpa, spring, spring-data, spring-data-jpa
Solution
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(…)`.
Problem
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! } ```