2016-06-27 2 views
0

Невозможно сохранить ссылку на дочерний объект.Исключение JPA/Hibernate: удаленный объект передан для сохранения при сохранении дочернего отношения @ManyToOne

Материнский родительский объект содержит дочерний employee_detail, который также имеет @ManyToOne, определенный для сохранения объекта Address.

структура таблицы

EMPLOYEE 
ID BIGINT(20) NOT NULL AUTO_INCREMENT 
NAME VARCHAR(100) NOT NULL 

EMPLOYEE_DETAIL 
ID    BIGINT(20) NOT NULL AUTO_INCREMENT 
EMPLOYEE_ID  BIGINT(20) NOT NULL 
ADDRESS_ID  BIGINT(20) NOT NULL 

Сущности

@Entity 
@Table(name = "employee") 
public class Employee { 
@Id 
@GeneratedValue(strategy = GenerationType.AUTO) 
@Column(name = "id") 
private Long id; 

@Column(name = "name") 
private String name; 

@OneToMany(mappedBy = "employee", cascade = CascadeType.ALL, fetch = FetchType.LAZY) 
private List <EmployeeDetail> employeeDetails = new ArrayList < >(); 

public void addEmployeeDetail(EmployeeDetail ed) { 
    employeeDetails.add(ed); 
    ed.setEmployee(this); 
} 

} 

@Entity 
@Table(name = "employee_detail") 
public class EmployeeDetail { 
@Id 
@GeneratedValue(strategy = GenerationType.AUTO) 
@Column(name = "id") 
private Long id; 

@ManyToOne(optional = false, cascade = CascadeType.PERSIST) 
@JoinColumn(name = "employee_id") 
private Employee employee; 

@ManyToOne(optional = false, cascade = CascadeType.PERSIST) 
@JoinColumn(name = "address_id") 
private Address address; 
} 

В методе РЕСТ контроллера:

public void saveEmployee(@RequestBody Employee employee) 
{ 
    EmployeeDetail employeeDetail = new EmployeeDetail(); 
    employeeDetail.setEmployee(employee); 

    for(EmployeeDetail ed : employee.getEmployeeDetails()) 
    { 
     Address address = addressService.findOne(ed.getAddress().getId()); 
     employeeDetail.setAddress(address); 
    } 

    employee.addEmployeeDetail(employeeDetail); 

    //SAVE 
    employeeService.create(employee); 
} 

Исключение:

nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.abc.Address 

Я не могу сохранить детали адреса с дочерней таблицы EmployeeDetail. Что здесь не так?

+0

Вы не можете сохранить отдельный экземпляр, получить постоянный экземпляр «Адрес» перед y ou обновить базу данных. –

+0

Извините, я обновил код поиска адреса. Но все та же проблема. – FakirTrappedInCode

+0

'cascade = CascadeType.PERSIST' в' ManyToOne' означает: когда упорство 'EmployeeDetail' также сохраняется в этом поле ... если существующий адрес и пытается быть сохраненным, возникает ошибка'org.hibernate.PersistentObjectException '. . –

ответ

1

Применить CascadeType.MERGE слишком для адреса объекта, как показано ниже

@ManyToOne(optional = false, cascade = {CascadeType.PERSIST,CascadeType.MERGE}) 
@JoinColumn(name = "address_id") 
private Address address; 

и использовать слияние() вместо того, чтобы сохраняться, чтобы сохранить ваши изменения

РЕДАКТИРОВАНИЕ

Да, вы должны применять CascadeType.MERGE для EmployeeDetail тоже .. иначе вы получите то же исключение, если у вас есть отдельный экземпляр EmployeeDetail в сети объектов, начиная с Employee.

Существует несколько сценариев, которые вам необходимо рассмотреть. Как объяснено ниже.

Когда вы вызываете persist() в корневой сущности, все ассоциации сущностей, сопоставленные с CascadeType.PERSIST, также передаются в persist() (переходное время). Теперь, если какая-либо из вашей сущности в графе объектов отделилась, persist() будет вызван и для этого объекта, и вы получите исключение для этого объекта.

Чтобы сохранить полный график объектов, включающих как отдельные, так и временные экземпляры, отметьте связь с CascadeType.MERGE и вызовите merge() в корневом сущности. Теперь операция merge() будет каскадирована для всех связанная с CascadeType.MERGE. Поведение слияния заключается в том, что он объединит любой отдельный объект, который будет передан ему существующей сущности в контексте персистентности, или если объект будет временным, он сделает его постоянным.

Поэтому, пытаясь сохранить граф сущности, вам нужно выбрать, использовать ли persist() или merge() на основе информации о том, будет ли граф объектов, который вы собираетесь сохранять, имеет только временные экземпляры или имеет как временные, так и отдельные instance.You также придется учитывать тип каскада, установленный для каждой ассоциации.

Для вашего кода .. Я вижу экземпляр адреса, который вы устанавливаете на экземпляр EmployeeDetail, отсоединяется, поскольку вы получаете его из другого уже выбранного экземпляра EmplyeeDetail.

Вы можете получить более подробную информацию по ссылке ниже

JPA EntityManager: Why use persist() over merge()?

Вы также можете расширить контекст сохранения путем создания беседы, если вы не хотите работать с обособленным instances.More об этом ниже

http://www.thoughts-on-java.org/unsychronized-persistencecontext-implement-conversations-jpa/

+0

Пожалуйста, объясните, для дальнейшего понимания проблемы. Также это должно применяться и для «Employee» в «EmployeeDetail». Если вы передаете существующий «Сотрудник» для «saveEmployee» (сотрудник сотрудника RRequestBody), OP получит то же исключение. –

+0

, пожалуйста, прочитайте мое редактирование выше для более подробного объяснения – Zulfi

+0

@ Zulfi добавление @ManyToOne (необязательно = false, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) для адреса по-прежнему дал мне тот же отдельный объект, который был передан для ошибки persist – FakirTrappedInCode

0
@Transacional 
public void saveEmployee(@RequestBody Employee employee){ 
... 
} 
+3

Пожалуйста, укажите, почему ваш ответ будет работать с небольшим количеством объяснений. – Tariqulazam

Смежные вопросы