Hibernate 递归映射 parent/child 结构 - 检索数据时发生 StackOverflowError

Hibernate recursive mapping parent/child structure - StackOverflowError when retrieve data

我正在使用 Hibernate 5.2。7.Final 和本机 Hibernate API。 我有一个具有递归关系的员工实体:

@Entity
public class Employee {

    @Id
    @GeneratedValue
    private Long employeeId;

    @Column
    private String firstname;

    @Column
    private String lastname;

    @ManyToOne(cascade={ CascadeType.ALL})
    @JoinColumn(name="manager_id")
    private Employee manager;

    @OneToMany(mappedBy="manager", cascade = CascadeType.ALL)
    private Set<Employee> subordinates = new HashSet<Employee>();
    // setters, getters, constructors
}

以下 table 是由 hibernate 创建的:

+------------+--------------+------+-----+---------+-------+
| Field      | Type         | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+-------+
| employeeId | bigint(20)   | NO   | PRI | NULL    |       |
| firstname  | varchar(255) | YES  |     | NULL    |       |
| lastname   | varchar(255) | YES  |     | NULL    |       |
| manager_id | bigint(20)   | YES  | MUL | NULL    |       |
+------------+--------------+------+-----+---------+-------+

我成功地坚持了经理和员工:

    Employee manager = new Employee("A", "B");

    Employee employee1 = new Employee("C", "D");
    Employee employee2 = new Employee("E", "E");

    employee1.setManager(manager1);
    employee2.setManager(manager1);

    Set<Employee> employees = new HashSet<>();
    employees.add(employee1);
    employees.add(employee2);

    manager.setSubordinates(employees);

    session.save(manager);

现在检索员工将导致 WhosebugError。

    Long id = manager.getEmployeeId();

    Query<Employee> query = ss.createQuery("from Employee e where e.employeeId = :employeeId", Employee.class);

    query.setParameter("employeeId", id);

    Employee retrieved = (Employee) query.uniqueResult();

线程异常 "main" java.lang.WhosebugError

at java.lang.Long.toString(Long.java:396)
at java.lang.Long.toString(Long.java:1032)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at objectModels.Employee.toString(Employee.java:82)
at java.lang.String.valueOf(String.java:2994)

我认为我可以持久化这样的Employee 对象图的方式非常方便。但我不知道如何从 table 中检索这样的图表。

问题:如何从底层 table 中有意义地检索员工信息。我认为解决它的方法是创建一个新的实体来查看员工 table like

    public class EmployeeView {
            private Long employeeId;
            private String firstname;
            private String manager_name;
            private Set<String> subordinate_names;
    }

我能做到吗?以及如何使用 JPA 注释?

我怀疑问题出在您的 Employee 实体中的 toString() 方法。

由于您没有提供任何代码,这里有一个清晰的示例,说明在 ParentChild 的更一般化的视角之间如何发生这种情况。

public class Parent {
  @OneToMany(mappedBy = "parent");
  List<Child> children;

  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append( "Parent{children=" );
    children.forEach( sb::apend );
    sb.append( "}" );
    return sb.toString();
}

public class Child {
  @ManyToOne
  private Parent parent;

  @Override
  public String toString() {
    return "Child{parent=" + parent + "}";
  }
}

现在代码中的某处是这样的:

System.out.println( parent.toString() );

这会导致同样的溢出问题,因为对 Parent 的调用会委托给 Child,后者又委托回 Parent,恶性循环继续。

您需要确定哪个在您的情况下最合乎逻辑,并且可能跳过或仅打印关联实体的标识符以避免此递归循环。