java - mkyong - ¿Cómo guardar una nueva entidad que remite la entidad existente en Spring JPA?
spring data rest (1)
Imagine los siguientes modelos:
Empleado:
@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>();
Proyecto:
@ManyToMany(mappedBy = "projects")
private Set<Employee> employees = new HashSet<Employee>();
Ahora si creo un nuevo empleado que se refiere a un proyecto existente y trato de persistir a ese empleado, aparece un error:
detached entity passed to persist: Project
Creo el empleado de la siguiente manera:
public void createNewEmployee(EmployeeDTO empDTO) {
Employee emp = new Employee();
// add stuff from DTO, including projects
repository.saveAndFlush(emp); // FAILS
}
y actualizo los existentes así:
public void updateEmployee(EmployeeDTO empDTO) {
Employee emp = repository.findOne(empDTO.getId());
// set stuff from DTO, including projects
repository.saveAndFlush(emp); // WORKS!
}
Supongo que estás interactuando con el repositorio sin ampliar los límites de la transacción de forma adecuada. Por defecto, el límite de la transacción (y, por lo tanto, la sesión) está en el nivel del método de repositorio. Esto hace que la instancia del Project
se EntityManager
, por lo que no se puede incluir en una operación de persistencia.
La solución aquí es extender el límite de transacción al cliente:
@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));
}
}
Este enfoque hace que la instancia del Project
permanezca como una entidad gestionada y, por lo tanto, la operación de persistencia se ejecute para la nueva instancia del Employee
se maneja correctamente.
La diferencia con las dos interacciones de repositorio es que en el segundo caso tendrá una instancia separada (ya se ha conservado, tiene un conjunto de id.), Donde, como en el primer ejemplo, tiene instancias completamente no administradas que no tienen una id. conjunto. La propiedad id es lo que hace que el repositorio diferencie entre llamar persist(…)
y merge(…)
. Entonces, el primer acercamiento causará que persist(…)
para ser activado, el segundo causará una merge(…)
.