如何使用JPA获取整个实体图

Man*_*oid 6 java orm jpa openjpa entitygraph

我使用JPA 2.0和OpenJPA作为底层实现.我有一个映射到自身的实体来表示实体之间的父子层次结构.一个实体最多可以有一个以上的子女,但只有一个父母.因此,没有父级的实体位于层次结构的顶层.我的目标是从数据表中获取所有层次结构.所以我的查询为:

SELECT e FROM MyEntity e where e.parent is null
Run Code Online (Sandbox Code Playgroud)

在MyEntity中,我完成了映射:

@ManyToOne
@JoinColumn(name="PARENT")
private MyEntity parent;

@OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
private List<MyEntity> children;
Run Code Online (Sandbox Code Playgroud)

当程序运行时,层次结构顶部的实体将填充其所有子项,但子实体不会获取其子项.我认为EAGER提取将填充整个实体图.但事实并非如此.在JPA 2.1中有EntityGraph ASAIK的功能.但是如何在JPA 2.0中实现呢?

Vla*_*cea 3

EAGER 仅在一个级别上工作,它并不是为了获取树状结构。

我建议您将一对多获取更改为 LAZY,因为EAGER 获取是一种代码味道,并像这样获取根实体:

SELECT e 
FROM MyEntity e 
LEFT JOIN FETCH e.children 
where e.parent is null
Run Code Online (Sandbox Code Playgroud)

您可以使用递归来获取带有附加子选择的所有图形。

void fetchAll(MyEntity root) {
    for(MyEntity child : root.children) {
        fetchAll(child);
    }
}
Run Code Online (Sandbox Code Playgroud)

更有效的方法是完全放弃子集合,并在 FK 关联上使用递归 CTE来获取给定树中所有实体的所有 id。然后,通过第二个 JPA 查询,您可以通过 id 获取所有实体,并通过匹配父项来重建树。

更新实际解决方案

我在 GitHub 上添加了一个测试来提供解决方案。考虑以下实体:

@Entity(name = "Node")
public class Node  {

    @Id
    @GeneratedValue
    private Long id;

    @ManyToOne
    @JoinColumn(name = "parent_id")
    private Node parent;

    //This must be transient as otherwise it will trigger an additional collection fetch
    //@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
    @Transient
    private List<Node> children = new ArrayList<>();

    public Node() {}

    public Node getParent() {
        return parent;
    }

    public List<Node> getChildren() {
        return children;
    }

    public void addChild(Node child) {
        children.add(child);
        child.parent = this;
    }
}
Run Code Online (Sandbox Code Playgroud)

下面的转换器可以根据需要重建整个树

Node root = (Node) doInHibernate(session -> {
    return session
        .createSQLQuery(
                "SELECT * " +
                "FROM Node " +
                "CONNECT BY PRIOR id = parent_id " +
                "START WITH parent_id IS NULL ")
        .addEntity(Node.class)
        .setResultTransformer(new ResultTransformer() {
            @Override
            public Object transformTuple(Object[] tuple, String[] aliases) {
                Node node = (Node) tuple[0];
                if(node.parent != null) {
                    node.parent.addChild(node);
                }
                return node;
            }

            @Override
            public List transformList(List collection) {
                return Collections.singletonList(collection.get(0));
            }
        })
        .uniqueResult();
});
Run Code Online (Sandbox Code Playgroud)

  • @lukaseder JPA 只能获取带有连接或子选择的集合。它无法从不同的子项重建集合,因为它无法保证所有子项均已获取 (2认同)
  • @VladMihalcea,很容易从基于链接的集合中重建图表:Hib。具有类型图,它知道为哪些值生成哈希值(pk/fk 对),并且将子集与父集合并很容易。对于哈希图,它是一种简单的树合并算法,如果您想让它变得非常快,可以从预先生成的字节代码中受益。没有逻辑原因说明子节点无法与图中的父节点合并,所有信息都在那里。 (2认同)