需要はほぼ無い EntityGraph からの Subgraph 取得


はじめに

JPA の EntityGraph を使うことで、クエリーで取得する対象をカスタマイズすることができます。

同じ Entity から様々なデータの見せ方が必要で、パフォーマンスの為に最小限のデータセットのみを取得したい場合などに EntityGraph が役に立ちます。

詳しくは以下を参照してください。

blog1.mammb.com


EntityGraphEntityManager から操作する場合は以下のようになります。

EntityGraph<Employee> graph = em.createEntityGraph(Employee.class);
graph.addSubgraph(Employee_.address).addAttributeNodes(Address_.city);

TypedQuery<Employee> query = em.createQuery(...);
query.setHint("jakarta.persistence.loadgraph", graph);
List<Employee> results = query.getResultList();

addSubgraph() で同じサブグラフを追加した場合は、後勝ちとなります。

つまり以下のように addSubgraph() を登録した場合は Address_.city のノードはフェッチ対象に含まれなくなります。

EntityGraph<Employee> graph = em.createEntityGraph(Employee.class);
graph.addSubgraph(Employee_.address).addAttributeNodes(Address_.city);
graph.addSubgraph(Employee_.address).addAttributeNodes(Address_.street);

以下のようにするか、

EntityGraph<Employee> graph = em.createEntityGraph(Employee.class);
Subgraph<Address>  subgraph = graph.addSubgraph(Employee_.address);

subgraph.addAttributeNodes(Address_.city);
subgraph.addAttributeNodes(Address_.street);

以下のようにすることになります。

subgraph.addAttributeNodes(Address_.city, Address_.street);

しかし、フェッチ範囲を以下のように部品として適用したい場合には少し困ります。

Consumer<EntityGraph<Employee>> foo = graph ->
    graph.addSubgraph(Employee_.address).addAttributeNodes(Address_.city);

Consumer<EntityGraph<Employee>> foo = graph ->
    graph.addSubgraph(Employee_.address).addAttributeNodes(Address_.street);


EntityGraph からの Subgraph 取得

以下のように EntityGraph に登録済みの Subgraph を抽出することができます。

public static <T, Y> Subgraph<Y> subgraph(EntityGraph<T> graph, Attribute<? super T, Y> attr, Class<Y> clazz) {
    return graph.getAttributeNodes().stream()
        .filter(node -> node.getAttributeName().equals(attr.getName()))
        .map(node -> node.getSubgraphs().get(clazz))
        .filter(Objects::nonNull)
        .findFirst()
        .orElseGet(() -> graph.addSubgraph(attr.getName(), clazz));
}

以下のようにすれば、必要なものを適宜組み合わせてフェッチ戦略を使っていくことができます。

Consumer<EntityGraph<Employee>> foo = graph ->
    subgraph(graph, Employee_.address, Address.class).addAttributeNodes(Address_.city);

Consumer<EntityGraph<Employee>> foo = graph ->
    subgraph(graph, Employee_.address, Address.class).addAttributeNodes(Address_.street);

以上、需要の無い話でした。

以下も合わせて参照ください。

blog1.mammb.com