异常JPA / Hibernate:在保存子@ManyToOne关系时传递的分离实体仍然存在

时间:2016-06-27 14:43:56

标签: spring spring-data spring-data-jpa

无法保存子对象引用。

Employee父对象包含子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;
}

在REST控制器方法中:

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保存地址详细信息。这有什么不对?

2 个答案:

答案 0 :(得分:1)

也为地址实体应用CascadeType.MERGE,如下所示

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

并使用merge()而不是persist来保存更改

修改

是的,您还必须为EmployeeDetail应用CascadeType.MERGE ..否则,如果您在从Employee开始的对象网络中有一个分离的EmployeeDetail实例,则会得到相同的异常。

您需要考虑几种情况。如下所述。

当您在根实体上调用persist()时,与CascadeType.PERSIST映射的所有实体关联也会传递给persist()(传递性持久性)。现在,如果对象图中的任何实体被分离,那么也将为该实体调用persist(),并且您将获得该实体的异常。

为了保存包含分离和瞬态实例的对象的完整图形,请标记与CascadeType.MERGE的关联并在根实体上调用merge()。现在merge()操作将级联到所有实体与CascadeType.MERGE映射的关联。合并的行为是它将任何传递给它的分离实体合并到持久化上下文中的现有实体,或者如果实体是瞬态的,它将使其持久化。

因此,在尝试保存实体图时,您需要根据信息选择是使用persist()还是merge(),以确定您要保存的实体图是仅具有瞬态实例还是具有瞬态实例和分离实例你还必须考虑每个协会的级联类型。

对于你的代码..我看到你在EmployeeDetail实例上设置的地址实例是分离的,因为你从另一个已经获取的EmplyeeDetail实例获取它。

您可以在以下链接中获得更多详细信息

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

如果您不想使用分离的实例,还可以通过创建对话来扩展持久性上下文。更多内容如下

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

答案 1 :(得分:0)

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