JPA/Hibernate 分离的实体传递给持久化
JPA/Hibernate detached entity passed to persist
JPA 和休眠的新手,所以请耐心等待我尝试绘制完整的图片。所以我有两个具有双向关系的 Java 对象。 Employee class 是所有者 class,Department class 是关系的反面。一个部门可以有很多员工,一个员工只能有一个部门。我指定 employee_id 作为员工实体的主键,department_id 作为部门实体的主键。我还想使用 department_id 作为员工 class 中的外键。
员工class
@Entity
@Table(name = "Employee")
public class Employee
implements java.io.Serializable
{
private static final long serialVersionUID = 1L;
//primary key
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "Employee_ID")
private Integer id;
//foreign key
@ManyToOne(cascade=CascadeType.ALL)
@JoinColumn(name = "Department_ID")
private Department department;
@Column(name = "Employee_name")
private String name;
public Employee() { }
public Employee(Integer id)
{
this.setId(id);
}
public void setDepartment(Department department){
this.department = department;
}
public Department getDepartment(){
return department;
}
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getEmployeeName(){
return name;
}
public void setEmployeeName(String name){
this.name = name;
}
部门class
@Entity
@Table(name = "Department")
public class Department
implements java.io.Serializable
{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "Department_ID")
private Integer id;
@Column(name = "Department_name")
private String name;
@OneToMany(mappedBy="department", cascade=CascadeType.ALL)
private Collection<Employee> employees = new ArrayList<Employee>();
public Collection<Employee> getEmployees() {return employees;}
public void setEmployees(Collection<Employee> e){ employees = e;}
public Department() { }
public Department(Integer id)
{
this.setId(id);
}
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getDepartmentName(){
return name;
}
public void setDepartmentName(String name){
this.name = name;
}
我用来生成两个对象并将它们保存在数据库中的函数。我正在测试的数据库是 H2,persistence.xml 文件配置为最初删除所有表,因此在调用以下函数之前所有表都是空的。
public static void populateTable() {
System.out.println("Populating tables");
Department d1 = new Department();
//d1.setId(1);
d1.setDepartmentName("finance");
//service.addDepartment(d1);
Employee e1 = new Employee();
e1.setEmployeeName("John");
//e1.setId(1);
//e1.setDepartment(d1);
//d1.getEmployees().add(e1);
service.addDepartment(d1);
service.addEmployee(e1);
}
最初我尝试自己设置创建对象的主要 ID,但出现了 "detached entity passed to persist" 错误。看了一个类似的question, I understand that if I try to set the unique identifier before persisting the object, JPA will think the entity is already present in the database and it will throw a "detached entity passed to persist error". So I commented out callers to setter functions and above code runs fine. However if I try to link an employee to a department like what I did with e1.setDepartment(d1);
I would get this error detached entity passed to persist: com.javatunes.jpa.catalog.Department
. I have read many blogs such as this one and this post,但是还是找不到解决这个问题的办法。还有一件事,这两个持久函数被定义为
public void addEmployee(Employee employee) {
em.persist(employee);
em.flush();
}
public void addDepartment(Department department){
em.persist(department);
em.flush();
}
错误发生在 em.persistent(员工)。
查看您的 Employee 实体,您已将 Department 配置为 Cascade ALL
这意味着只要 Employee 实体是 PERSIST, MERGE, REMOVE, REFRESH, DETACH
就会对 Department 做同样的事情。
现在回到你的问题。在你的 populateTable
函数中
您已经创建了 Department
和 Employee
实体
你需要像这样 link Department
和 Employee
e1.setDepartment(d1);
最后持久化员工,它将为您持久化员工和部门
我认为问题出在您的服务上 class。因为当你保存 d1
service.addDepartment(d1);
时你的服务 class 是 @Transactional
然后当函数 returns 然后 d1
被分离并且它不再在持久状态,然后在下一行尝试保存 e1
service.addEmployee(e1);
包括 d1.getEmployees().add(e1)
时,显然会得到 detached entity passed to persist:
这个错误。因为您的 d1
对象已经处于分离状态,并且您正试图将其保存在另一个 @Transactional
方法中。我认为您可以将两种服务方法合并为一个以保存部门和员工,并且可以简单地忽略此错误,或者您可以在下一个服务方法中使用 merge
操作,如下所示:
public void addDepartment(Department department) {
em.merge(department);
em.flush();
}
感谢@Ashish451,错误在于在我的服务代码中,我保留了 department (d1)。但是,如果我尝试先 link 员工 (e1) 和部门 (d1) 然后保留员工,JPA 将不会高兴,因为部门 (d1) 已经保留并且 persist()
仅有效在瞬态对象或新创建的实例上。
我在 spring 引导项目中遇到了类似的问题,但这里的解决方案很容易工作。我意识到只要你在父对象之前持久化一个子对象,你总是会得到分离的持久性异常,即你永远不应该在子实体上放置 Cascade.ALL 约束。
JPA 和休眠的新手,所以请耐心等待我尝试绘制完整的图片。所以我有两个具有双向关系的 Java 对象。 Employee class 是所有者 class,Department class 是关系的反面。一个部门可以有很多员工,一个员工只能有一个部门。我指定 employee_id 作为员工实体的主键,department_id 作为部门实体的主键。我还想使用 department_id 作为员工 class 中的外键。
员工class
@Entity
@Table(name = "Employee")
public class Employee
implements java.io.Serializable
{
private static final long serialVersionUID = 1L;
//primary key
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "Employee_ID")
private Integer id;
//foreign key
@ManyToOne(cascade=CascadeType.ALL)
@JoinColumn(name = "Department_ID")
private Department department;
@Column(name = "Employee_name")
private String name;
public Employee() { }
public Employee(Integer id)
{
this.setId(id);
}
public void setDepartment(Department department){
this.department = department;
}
public Department getDepartment(){
return department;
}
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getEmployeeName(){
return name;
}
public void setEmployeeName(String name){
this.name = name;
}
部门class
@Entity
@Table(name = "Department")
public class Department
implements java.io.Serializable
{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "Department_ID")
private Integer id;
@Column(name = "Department_name")
private String name;
@OneToMany(mappedBy="department", cascade=CascadeType.ALL)
private Collection<Employee> employees = new ArrayList<Employee>();
public Collection<Employee> getEmployees() {return employees;}
public void setEmployees(Collection<Employee> e){ employees = e;}
public Department() { }
public Department(Integer id)
{
this.setId(id);
}
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getDepartmentName(){
return name;
}
public void setDepartmentName(String name){
this.name = name;
}
我用来生成两个对象并将它们保存在数据库中的函数。我正在测试的数据库是 H2,persistence.xml 文件配置为最初删除所有表,因此在调用以下函数之前所有表都是空的。
public static void populateTable() {
System.out.println("Populating tables");
Department d1 = new Department();
//d1.setId(1);
d1.setDepartmentName("finance");
//service.addDepartment(d1);
Employee e1 = new Employee();
e1.setEmployeeName("John");
//e1.setId(1);
//e1.setDepartment(d1);
//d1.getEmployees().add(e1);
service.addDepartment(d1);
service.addEmployee(e1);
}
最初我尝试自己设置创建对象的主要 ID,但出现了 "detached entity passed to persist" 错误。看了一个类似的question, I understand that if I try to set the unique identifier before persisting the object, JPA will think the entity is already present in the database and it will throw a "detached entity passed to persist error". So I commented out callers to setter functions and above code runs fine. However if I try to link an employee to a department like what I did with e1.setDepartment(d1);
I would get this error detached entity passed to persist: com.javatunes.jpa.catalog.Department
. I have read many blogs such as this one and this post,但是还是找不到解决这个问题的办法。还有一件事,这两个持久函数被定义为
public void addEmployee(Employee employee) {
em.persist(employee);
em.flush();
}
public void addDepartment(Department department){
em.persist(department);
em.flush();
}
错误发生在 em.persistent(员工)。
查看您的 Employee 实体,您已将 Department 配置为 Cascade ALL
这意味着只要 Employee 实体是 PERSIST, MERGE, REMOVE, REFRESH, DETACH
就会对 Department 做同样的事情。
现在回到你的问题。在你的 populateTable
函数中
您已经创建了 Department
和 Employee
实体
你需要像这样 link Department
和 Employee
e1.setDepartment(d1);
最后持久化员工,它将为您持久化员工和部门
我认为问题出在您的服务上 class。因为当你保存 d1
service.addDepartment(d1);
时你的服务 class 是 @Transactional
然后当函数 returns 然后 d1
被分离并且它不再在持久状态,然后在下一行尝试保存 e1
service.addEmployee(e1);
包括 d1.getEmployees().add(e1)
时,显然会得到 detached entity passed to persist:
这个错误。因为您的 d1
对象已经处于分离状态,并且您正试图将其保存在另一个 @Transactional
方法中。我认为您可以将两种服务方法合并为一个以保存部门和员工,并且可以简单地忽略此错误,或者您可以在下一个服务方法中使用 merge
操作,如下所示:
public void addDepartment(Department department) {
em.merge(department);
em.flush();
}
感谢@Ashish451,错误在于在我的服务代码中,我保留了 department (d1)。但是,如果我尝试先 link 员工 (e1) 和部门 (d1) 然后保留员工,JPA 将不会高兴,因为部门 (d1) 已经保留并且 persist()
仅有效在瞬态对象或新创建的实例上。
我在 spring 引导项目中遇到了类似的问题,但这里的解决方案很容易工作。我意识到只要你在父对象之前持久化一个子对象,你总是会得到分离的持久性异常,即你永远不应该在子实体上放置 Cascade.ALL 约束。