【问题标题】:Bidirectional relationships in JPAJPA 中的双向关系
【发布时间】:2015-08-24 14:17:52
【问题描述】:

我遗漏了一些非常基本的东西。下面给出了两个实体Department(反面)和Employee(拥有方)形成了从DepartmentEmployee的一对多关系。

部门.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&lt;Employee&gt;) 自动反映上述每个操作期间的正确状态。

  • Employee 实体被持久化时,它会在列表中列出 反面(我没有明确添加新持久的 Employee 实体到反面的List&lt;Employee&gt;)。
  • Employee实体被合并时,对该实体所做的更改会自动由反面List&lt;Employee&gt;中的对应实体反映出来(我没有明确更改员工列表所持有的对应实体(@ 987654337@) 在反面)。
  • 类似地,当一个Employee 实体被移除时,它也会被移除 从关系反面的列表中(我不是 从反面的列表中显式删除该实体)。

我目前正在使用 EcliseLink 2.6.0。为什么我会看到与以下文本不匹配的这种行为?

与所有双向关系一样,它是您的对象模型和 应用程序有责任维护两者的关系 方向。 如果您在一侧添加或删除,JPA 没有魔法 集合,您还必须从另一侧添加或删除, 见object corruption。从技术上讲,数据库将被更新 如果您仅从拥有方添加/删除,则正确 关系,但是您的对象模型将不同步,这 可能会导致问题。

http://en.wikibooks.org/wiki/Java_Persistence/ManyToMany#Bi-directional_Many_to_Many

【问题讨论】:

  • 哇,我从未见过有人抱怨软件做得比他们预期的更多

标签: hibernate jpa eclipselink jpa-2.1 bidirectional-relation


【解决方案1】:

这意味着在您的特定示例中,如果您更改代码以将员工添加到部门(而不是设置部门的其他方式),那么您会注意到这不会自动在员工。您将不得不编写代码来明确执行此操作。

因此,即使您显示的特定代码路径确实有效,但这并不意味着您可以依赖它。我可以猜一下为什么会这样 - 集合是延迟加载的,并且由于对象在加载集合之前被持久化,因此它能够从数据库中提取正确的数据。

最好的解决方案是注意文档中的建议,并在双向关系的两端正确设置状态,尽管有性能考虑(稍后可以微调)。

【讨论】:

  • +1,是的,getEmployeeList() 方法启动的员工列表的延迟加载可能是在调用部门员工的 SQL 选择,因此它将反映所做的任何更改。打开 SQL 日志记录可能会证实这一点。
  • 我未能彻底理解第一段中的部分,“如果您更改代码以将员工添加到部门(而不是设置部门的其他方式),那么您将请注意,这不会自动为员工设置部门。您必须编写代码来明确执行此操作。"
  • 即,如果改为这样做: entityManager.getReference(Department.class, 1L).getEmployeeList().add(employee)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-29
相关资源
最近更新 更多