Подтвердить что ты не робот

Как сохранить новый объект, который ссылается на существующий объект в Spring JPA?

Представьте себе следующие модели:

Сотрудник:

@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>();

Проект:

@ManyToMany(mappedBy = "projects")
private Set<Employee> employees = new HashSet<Employee>();

Теперь, если я создаю нового сотрудника, который ссылается на существующий проект и пытается сохранить этого сотрудника, я получаю сообщение об ошибке:

detached entity passed to persist: Project

Я создаю сотрудника следующим образом:

public void createNewEmployee(EmployeeDTO empDTO) {

  Employee emp = new Employee();
  // add stuff from DTO, including projects

  repository.saveAndFlush(emp);  // FAILS
}

и я обновляю существующие:

public void updateEmployee(EmployeeDTO empDTO) {

   Employee emp = repository.findOne(empDTO.getId());
   // set stuff from DTO, including projects

   repository.saveAndFlush(emp);  // WORKS!
}
4b9b3361

Ответ 1

Я предполагаю, что вы взаимодействуете с репозиторией без надлежащего расширения границ транзакций. По умолчанию граница транзакции (и, следовательно, сеанса) находится на уровне метода репозитория. Это приводит к отключению экземпляра Project от EntityManager, поэтому он не может быть включен в операцию persist.

Решением здесь является расширение границы транзакции для клиента:

@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));
  }
}

Этот подход заставляет экземпляр Project оставаться управляемым объектом и, таким образом, выполняется операция persist для корректного обработки нового экземпляра Employee.

Разница с двумя взаимодействиями репозитория заключается в том, что во втором случае у вас будет отдельный экземпляр (он уже сохранен, имеет набор идентификаторов), где, как и в первом примере, у вас есть полностью неуправляемые экземпляры, которые не имеют набор идентификаторов. Свойство id - это то, что заставляет репозиторий различать вызовы persist(…) и merge(…). Таким образом, первый подход приведет к срабатыванию persist(…), второй приведет к merge(…).