【问题标题】:Exception JPA/Hibernate: detached entity passed to persist while saving child @ManyToOne relation异常 JPA/Hibernate:在保存子 @ManyToOne 关系时传递的分离实体持续存在
【发布时间】:2016-10-29 15:25:30
【问题描述】:

无法保存子对象引用。

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

【问题讨论】:

  • 您不能持久化分离的实例,在更新数据库之前获取Address 的持久化实例。
  • 抱歉,我更新了地址检索代码。但还是同样的问题。
  • cascade = CascadeType.PERSISTManyToOne 中的意思是:当持久化EmployeeDetail 时,还要持久化这个字段...如果地址存在并且试图持久化,一个'org.hibernate.PersistentObjectException' 是抛出...
  • 我删除了地址字段中定义的大小写注释。现在得到 com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'employee_id' cannot be null
  • 你必须先保存Employee,然后用刚刚持久化的Employee创建EmployeeDetail

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


【解决方案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 实例上设置的 Address 实例已分离,因为您从另一个已获取的 EmplyeeDetail 实例获取它。

您可以通过以下链接获得更多详细信息

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

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

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

【讨论】:

  • 请解释一下以进一步理解问题。这也应该适用于EmployeeDetail 中的Employee。如果您将现有的 Employee 传递给 saveEmployee(@RequestBody Employee employee) OP 将得到相同的异常。
  • @Zulfi 添加 @ManyToOne(optional = false, cascade = {CascadeType.PERSIST,CascadeType.MERGE}) 到地址仍然给了我传递给持久错误的相同分离实体
  • @pondicherryFellow 你必须使用 merge() 而不是 persist()
  • 我使用的是 Spring Data JPA,所以我只使用了 save 方法。
  • @Pondiccherry Fellow 查看链接,这可能会有所帮助stackoverflow.com/questions/16559407/…
猜你喜欢
  • 1970-01-01
  • 2016-06-27
  • 2012-06-13
  • 1970-01-01
  • 2018-03-26
  • 2014-10-13
  • 2016-10-20
  • 2020-09-28
  • 2017-10-21
相关资源
最近更新 更多