JPA 中的双向关系
Bidirectional relationships in JPA
我遗漏了一些非常基本的东西。下面给出两个实体 Department
(反面)和 Employee
(拥有方)形成从 Department
到 Employee
.
的一对多关系
Department.java
@Entity
@Table(catalog = "testdb", schema = "", uniqueConstraints = {
@UniqueConstraint(columnNames = {"department_id"})})
public class Department implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "department_id", nullable = false)
private Long departmentId;
@Column(name = "department_name", length = 255)
private String departmentName;
@Column(length = 255)
private String location;
@OneToMany(mappedBy = "department", fetch = FetchType.LAZY)
private List<Employee> employeeList = new ArrayList<Employee>(0);
private static final long serialVersionUID = 1L;
// Constructors + getters + setters + hashcode() + equals() + toString().
// No defensive link (relationship) management methods have yet been added to.
// CascadeType is also kept at a distance for now.
}
Employee.java
@Entity
@Table(catalog = "testdb", schema = "", uniqueConstraints = {
@UniqueConstraint(columnNames = {"employee_id"})})
public class Employee implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "employee_id", nullable = false)
private Long employeeId;
@Column(name = "employee_name", length = 255)
private String employeeName;
@JoinColumn(name = "department_id", referencedColumnName = "department_id")
@ManyToOne(fetch = FetchType.LAZY)
private Department department;
private static final long serialVersionUID = 1L;
// Constructors + getters + setters + hashcode() + equals() + toString().
// No defensive link (relationship) management methods have yet been added to.
// CascadeType is also kept at a distance for now.
}
下面给出了一些在无状态 EJB(使用 CMT)中分别执行持久化、合并和删除操作的方法。
public List<Employee> persist() {
Employee employee = new Employee();
employee.setEmployeeName("a");
employee.setDepartment(entityManager.getReference(Department.class, 1L));
entityManager.persist(employee);
return employee.getDepartment().getEmployeeList();
}
public List<Employee> merge(Employee employee) {
employee.setEmployeeName("b");
employee.setDepartment(entityManager.getReference(Department.class, 1L));
return entityManager.merge(employee).getDepartment().getEmployeeList();
}
public List<Employee> remove(Employee employee) {
entityManager.remove(entityManager.contains(employee) ? employee : entityManager.merge(employee));
return entityManager.getReference(Employee.class, employee.getEmployeeId()).getDepartment().getEmployeeList();
}
public Employee getEmployeeById(Long id) {
return entityManager.find(Employee.class, id);
}
这些方法由关联的应用程序客户端在非事务环境中依次(一个接一个)调用。
List<Employee> persistedList = employeeSessionBean.persist();
for (Employee employee : persistedList) {
System.out.println(employee.getEmployeeId() + " : " + employee.getEmployeeName());
}
List<Employee> mergedList = employeeSessionBean.merge(employeeSessionBean.getEmployeeById(23L));
for (Employee employee : mergedList) {
System.out.println(employee.getEmployeeId() + " : " + employee.getEmployeeName());
}
List<Employee> listAfterRemoving = employeeSessionBean.remove(employeeSessionBean.getEmployeeById(23L));
for (Employee employee : listAfterRemoving) {
System.out.println(employee.getEmployeeId() + " : " + employee.getEmployeeName());
}
关系反面的列表(List<Employee>
)会自动反映上述每个操作期间的正确状态。
- 当一个
Employee
实体被持久化时,它被列在列表中
反面(我没有明确添加新坚持的
Employee
实体到反面的 List<Employee>
。
- 合并
Employee
实体时,对实体所做的更改会自动反映在反面List<Employee>
中的相应实体(我没有明确更改由List<Employee>
持有的相应实体反面的员工名单(List<Employee>
)。
- 同样,当一个
Employee
实体被移除时,它也会被移除
从关系反面的列表中(我不是
从反面的列表中明确删除该实体)。
我目前使用的是 EcliseLink 2.6.0。为什么我看到这样的行为似乎与以下文本不匹配?
As with all bi-directional relationships it is your object model's and
application's responsibility to maintain the relationship in both
direction. There is no magic in JPA, if you add or remove to one side
of the collection, you must also add or remove from the other side,
see object corruption. Technically the database will be updated
correctly if you only add/remove from the owning side of the
relationship, but then your object model will be out of synch, which
can cause issues.
http://en.wikibooks.org/wiki/Java_Persistence/ManyToMany#Bi-directional_Many_to_Many
这意味着对于您的特定示例,如果您更改代码以将员工添加到部门(而不是设置部门的其他方式),那么您会注意到这不会自动设置部门在员工身上。您将不得不编写代码来显式执行此操作。
因此,即使您显示的特定代码路径确实有效,也不意味着您可以依赖它。我可以猜猜为什么这样做 - 集合是延迟加载的,并且由于对象在集合加载之前被持久化,所以它能够从数据库中提取正确的数据。
最好的解决方案是听取文档中的建议并在双向关系的两端正确设置状态,尽管有性能方面的考虑(稍后可以微调)。
我遗漏了一些非常基本的东西。下面给出两个实体 Department
(反面)和 Employee
(拥有方)形成从 Department
到 Employee
.
Department.java
@Entity
@Table(catalog = "testdb", schema = "", uniqueConstraints = {
@UniqueConstraint(columnNames = {"department_id"})})
public class Department implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "department_id", nullable = false)
private Long departmentId;
@Column(name = "department_name", length = 255)
private String departmentName;
@Column(length = 255)
private String location;
@OneToMany(mappedBy = "department", fetch = FetchType.LAZY)
private List<Employee> employeeList = new ArrayList<Employee>(0);
private static final long serialVersionUID = 1L;
// Constructors + getters + setters + hashcode() + equals() + toString().
// No defensive link (relationship) management methods have yet been added to.
// CascadeType is also kept at a distance for now.
}
Employee.java
@Entity
@Table(catalog = "testdb", schema = "", uniqueConstraints = {
@UniqueConstraint(columnNames = {"employee_id"})})
public class Employee implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "employee_id", nullable = false)
private Long employeeId;
@Column(name = "employee_name", length = 255)
private String employeeName;
@JoinColumn(name = "department_id", referencedColumnName = "department_id")
@ManyToOne(fetch = FetchType.LAZY)
private Department department;
private static final long serialVersionUID = 1L;
// Constructors + getters + setters + hashcode() + equals() + toString().
// No defensive link (relationship) management methods have yet been added to.
// CascadeType is also kept at a distance for now.
}
下面给出了一些在无状态 EJB(使用 CMT)中分别执行持久化、合并和删除操作的方法。
public List<Employee> persist() {
Employee employee = new Employee();
employee.setEmployeeName("a");
employee.setDepartment(entityManager.getReference(Department.class, 1L));
entityManager.persist(employee);
return employee.getDepartment().getEmployeeList();
}
public List<Employee> merge(Employee employee) {
employee.setEmployeeName("b");
employee.setDepartment(entityManager.getReference(Department.class, 1L));
return entityManager.merge(employee).getDepartment().getEmployeeList();
}
public List<Employee> remove(Employee employee) {
entityManager.remove(entityManager.contains(employee) ? employee : entityManager.merge(employee));
return entityManager.getReference(Employee.class, employee.getEmployeeId()).getDepartment().getEmployeeList();
}
public Employee getEmployeeById(Long id) {
return entityManager.find(Employee.class, id);
}
这些方法由关联的应用程序客户端在非事务环境中依次(一个接一个)调用。
List<Employee> persistedList = employeeSessionBean.persist();
for (Employee employee : persistedList) {
System.out.println(employee.getEmployeeId() + " : " + employee.getEmployeeName());
}
List<Employee> mergedList = employeeSessionBean.merge(employeeSessionBean.getEmployeeById(23L));
for (Employee employee : mergedList) {
System.out.println(employee.getEmployeeId() + " : " + employee.getEmployeeName());
}
List<Employee> listAfterRemoving = employeeSessionBean.remove(employeeSessionBean.getEmployeeById(23L));
for (Employee employee : listAfterRemoving) {
System.out.println(employee.getEmployeeId() + " : " + employee.getEmployeeName());
}
关系反面的列表(List<Employee>
)会自动反映上述每个操作期间的正确状态。
- 当一个
Employee
实体被持久化时,它被列在列表中 反面(我没有明确添加新坚持的Employee
实体到反面的List<Employee>
。 - 合并
Employee
实体时,对实体所做的更改会自动反映在反面List<Employee>
中的相应实体(我没有明确更改由List<Employee>
持有的相应实体反面的员工名单(List<Employee>
)。 - 同样,当一个
Employee
实体被移除时,它也会被移除 从关系反面的列表中(我不是 从反面的列表中明确删除该实体)。
我目前使用的是 EcliseLink 2.6.0。为什么我看到这样的行为似乎与以下文本不匹配?
As with all bi-directional relationships it is your object model's and application's responsibility to maintain the relationship in both direction. There is no magic in JPA, if you add or remove to one side of the collection, you must also add or remove from the other side, see object corruption. Technically the database will be updated correctly if you only add/remove from the owning side of the relationship, but then your object model will be out of synch, which can cause issues.
http://en.wikibooks.org/wiki/Java_Persistence/ManyToMany#Bi-directional_Many_to_Many
这意味着对于您的特定示例,如果您更改代码以将员工添加到部门(而不是设置部门的其他方式),那么您会注意到这不会自动设置部门在员工身上。您将不得不编写代码来显式执行此操作。
因此,即使您显示的特定代码路径确实有效,也不意味着您可以依赖它。我可以猜猜为什么这样做 - 集合是延迟加载的,并且由于对象在集合加载之前被持久化,所以它能够从数据库中提取正确的数据。
最好的解决方案是听取文档中的建议并在双向关系的两端正确设置状态,尽管有性能方面的考虑(稍后可以微调)。