从父JPA获取所有子节点和子子节点

3

我有一个TABLE表,其中可能有相同类型的父级。在Java中,子级可以访问父级,但父级没有子级列表。

在MySQL中,我能够创建以下查询,给我父对象的子对象和孙对象,但我无法将其翻译成JPA Java语言。

我该如何翻译这个查询:

SELECT * 
FROM TABLE AS T1 
INNER JOIN 
  (SELECT id FROM TABLE WHERE TABLE.parentId = 966) AS T2 
  ON T2.id = T1.parentId OR T1.parentId = 966 
GROUP BY T1.id

使用Criteria Builder和Criteria Query转换为Java语言后,我已经尝试了以下代码:

CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<TABLE> cq = cb.createQuery(TABLE.class);
    Root<TABLE> tABLE= cq.from(TABLE.class);

    Predicate result = cb.conjunction();

    Join<TABLE, TABLE> TABLEJoin = tABLE.join("parent", JoinType.INNER);
    result = cb.and(result, cb.equal(genericLocationJoin.get("parent"), location));
em.createQuery(cq.select(tAble).where(result)).getResultList();

但这只给了我直接子元素,没有给我子子元素。

谢谢你的帮助。

实体:

@Entity
@Table(name = "table")
public final class TABLE {

    @Column(nullable = false, unique = true, length = 20)
    private String externalId;

    @Column(nullable = false, length = 50)
    private String name;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "parentId", nullable = true)
    private TABLE parent;
}

发布你的实体。 - Alan Hay
1个回答

0

您可以通过使关系双向化并编写递归方法来遍历树,在领域模型中处理此问题。其中一个优点是它可以处理子级到任何级别。

这将类似于以下内容,然后对于任何实例,您可以执行以下操作:

SomeEntity e = //;
e.getChildren(); //only direct children
e.getAllChildren(); //all children

实体:

@Entity
@Table(name = "some_entity")
public final class SomeEntity {

    @Column(nullable = false, unique = true, length = 20)
    private String externalId;

    @Column(nullable = false, length = 50)
    private String name;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "parentId", nullable = true)
    private SomeEntity parent;
    
    @OneToMany(mappedBy = "parent")
    private List<SomeEntity> children; //or Set<>
    
    //returns direct children
    public List<SomeEntity> getChildren(){
        return children;
    } 
    
    //returns all children to any level
    public List<SomeEntity> getAllChildren(){
        getAllChildren(this);
    }
    
    //recursive function to walk the tree
    private List<SomeEntity> getAllChildren(SomeEntity parent){
        List<SomeEntity> allChidren = new ArrayList<>();
        
        for(SomeEntity child : parent.getChildren()){
            allChildren.add(child);
            allChildren.addAll(getAllChildren(child);
        }
        
        return allChildren;
    }
}

1
非常感谢,我认为这对于这种情况已经是足够好的解决方案了。问题是我一直试图在一个查询中完成所有操作。 - André Pedro
好的,我进行了轻微的编辑。递归部分尚未测试,但经过编辑后应该没问题。 - Alan Hay
这个解决方案的问题在于它会对数据库产生很大的影响,因为对象总是会请求子级,在大多数情况下这并不是应该做的。默认情况下是有一个父级而不是请求子级,但在某些情况下需要这种关系。这就是为什么我试图实现一个可以在需要时使用而不是每次请求实体时都使用的查询。 - André Pedro
子集合是惰性加载的,所以我不确定我是否理解。 - Alan Hay
我看到一种做法是为每个实体添加一个路径,例如,ID为1的根父级将是/,它的子级将具有/1/等路径,这样您就可以编写查询以获取每个父级的所有子级。 - André Pedro
有人可以帮忙解决以下问题吗?https://stackoverflow.com/questions/76154920/getting-handler-dispatch-failed-nested-exception-is-java-lang-stackoverflowerro - bharathi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接