JPA和hibernate的新手,所以请在我尝试绘制完整的图片时请耐心等待。所以我有两个具有双向关系的Java对象。 Employee类是拥有类,Department类是关系的反面。部门可以有很多员工,员工只能有一个部门。我将employee_id指定为employee实体的主键,将department_id指定为department实体的主键。我还想使用department_id作为employee类中的外键。
员工类
@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;
}
系类
@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,并且我正在“将已分离的实体传递给持久性”错误。在阅读了类似的question之后,我明白如果我在持久化对象之前尝试设置唯一标识符,JPA会认为该实体已经存在于数据库中并且它将抛出一个“分离的实体传递给持久性错误” 。所以我将调用者注释掉了setter函数,上面的代码运行正常。但是,如果我尝试将员工链接到部门,就像我使用e1.setDepartment(d1);
所做的那样,我会收到此错误detached entity passed to persist: com.javatunes.jpa.catalog.Department
。我读过许多博客,例如this one和this post,但我仍然无法找到解决此问题的方法。另外两个持久化函数被定义为
public void addEmployee(Employee employee) {
em.persist(employee);
em.flush();
}
public void addDepartment(Department department){
em.persist(department);
em.flush();
}
错误是在em.persistent(员工)处引发的。
答案 0 :(得分:2)
查看您的Employee实体,您已将Department配置为Cascade ALl
这意味着只要员工实体PERSIST, MERGE, REMOVE, REFRESH, DETACH
对部门执行相同操作。
现在回到你的问题。在populateTable
功能中
您已创建Department
和Employee
实体
您需要像这样链接Department
和Employee
e1.setDepartment(d1);
并最终坚持员工,这将为员工和部门保留
答案 1 :(得分:1)
我认为问题是你的服务类。由于保存@Transactional
d1
时服务类为service.addDepartment(d1);
,因此当函数返回时,d1
将被分离并且不再处于持久状态,然后在下一行当您尝试保存包含e1
的{{1}} service.addEmployee(e1);
时,您显然会收到d1.getEmployees().add(e1)
此错误。因为您的detached entity passed to persist:
对象已处于分离状态,并且您尝试将其保留在另一个d1
方法中。我认为您可以将两种服务方法合并为一种以节省部门和员工,并且可以忽略此错误,或者您可以在下一个服务方法中使用@Transactional
操作,如下所示:
merge
答案 2 :(得分:1)
在我的春季启动项目中,我遇到了类似的问题,但是这里的解决方案很轻松。我意识到,只要在父对象之前持久保留子对象,即总是不要将Cascade.ALL约束放在子实体上,总是会得到分离的持久性异常。
答案 3 :(得分:0)
感谢@ Ashish451,错误在于我的服务代码中我坚持部门(d1)。但是,如果我首先尝试链接员工(e1)和部门(d1)然后坚持员工,JPA将不会高兴,因为部门(d1)已经被保留,persist()
仅适用于临时对象或新创建的实例。