JPA EntityGraph 如何允许在运行时选择实体图?

How does JPA EntityGraph allow chosing the entity graph at runtime?

我看到各种 post 描述 JPA EntityGraph 允许在 运行 时选择图形。我也不完全清楚这是指什么。

出于真诚和尊重,我想将这篇有用的文章作为参考:https://www.baeldung.com/jpa-entity-graph。 (大多数 JPA 用户可能已经用过它了。)

文章引用-

EntityGraph allows grouping the related persistence fields which we want to retrieve and lets us choose the graph type at runtime.

并在结论部分再次巩固上述陈述。

In this article, we've explored using the JPA Entity Graph to dynamically fetch an Entity and its associations.

The decision is made at runtime in which we choose to load or not the related association.

正如我们在文章 (5.1) 中看到的 - EntityGraphs 使用 Annotations 定义如下 -
5.1。使用注释定义实体图

@NamedEntityGraph(
  name = "post-entity-graph",
  attributeNodes = {
    @NamedAttributeNode("subject"),
    @NamedAttributeNode("user"),
    @NamedAttributeNode("comments"),
  }
)
@Entity
public class Post {

    @OneToMany(mappedBy = "post")
    private List<Comment> comments = new ArrayList<>();
    
    //...
}

@NameEntityGraph 注释是在编译时定义的,我在这里看不到任何 运行time 或 dynamic。

但在 5.2 - 实体图是使用 api 或以编程方式定义的 -
5.2。使用 JPA 定义实体图 API

 EntityGraph<Post> entityGraph = entityManager.createEntityGraph(Post.class);
 entityGraph.addAttributeNodes("subject");
 entityGraph.addAttributeNodes("user");

在 5.2 方法中,我看到可以使用一些逻辑动态选择节点。那么这种方法就是所谓的“动态获取”和“运行基于时间”。 还是我遗漏了一些东西,还有更多要了解的东西。

进一步采用 6 中给出的方法。使用实体图
例如:

EntityGraph entityGraph = entityManager.getEntityGraph("post-entity-graph");
Map<String, Object> properties = new HashMap<>();
properties.put("javax.persistence.fetchgraph", entityGraph);
Post post = entityManager.find(Post.class, id, properties);

都是程序化的,因此可以在 运行 时间内更改,即它们可以说是动态的。
但是上面文章中遗漏了一种方法,但这里提到了 - https://www.baeldung.com/spring-data-jpa-named-entity-graphs,如下所示,似乎不符合动态标准。

public interface ItemRepository extends JpaRepository<Item, Long> {

    @EntityGraph(value = "Item.characteristics")
    Item findByName(String name);
}

那么动态方法是指 5.2 风格还是它甚至暗示 5.1 风格。

在 Baeldung 文章中,第 5 节 仅介绍了定义图的各种方法,并未过多强调定义本身的 dynamic/non-dynamic 性质。

第 5.1 节 中,图的定义是静态的,但本节仅介绍如何定义图,然后在动态构建图的典型情况下使用该图不是真的有必要。本节展示了使用 JOIN FETCH 部分构建 HQL/JPA-QL 查询的旧方法的替代方法。

@NamedEntityGraph(
  name = "post-entity-graph",
  attributeNodes = {
    @NamedAttributeNode("subject"),
    @NamedAttributeNode("user"),
    @NamedAttributeNode("comments"),
  }
)
@Entity
public class Post {

    @OneToMany(mappedBy = "post")
    private List<Comment> comments = new ArrayList<>();
    
    //...
}

然后,第 6 节 告诉您如何以各种方式使用前面定义的实体图。

// Getting the "statically" defined graph (from annotation)
EntityGraph entityGraph = entityManager.getEntityGraph("post-entity-graph");

// Then using the graph
Map<String, Object> properties = new HashMap<>();
properties.put("javax.persistence.fetchgraph", entityGraph);
Post post = entityManager.find(Post.class, id, properties);

当然,您可以将第一行换成第 5.2 节中演示的完全动态构建的图:

// Building the graph dynamically
EntityGraph<Post> entityGraph = entityManager.createEntityGraph(Post.class);
entityGraph.addAttributeNodes("subject");
entityGraph.addAttributeNodes("user");


// Then using the graph
Map<String, Object> properties = new HashMap<>();
properties.put("javax.persistence.fetchgraph", entityGraph);
Post post = entityManager.find(Post.class, id, properties);

在这两种情况下,您都为查询提供了一个 EntityGraph 对象。

您不能将动态实体图与 spring-data 一起使用,因为 JpaRepository 没有传递实体图的方法,例如

Optional<T> findById(ID id, EntityGraph entityGraph);

使用自定义 JPA 存储库

您可以为此使用原始 JPA,方法是创建自定义存储库并使用具有 EntityManager 的实体图。

使用 spring-data-jpa-entity-graph

有一个更方便的方法是使用库 spring-data-jpa-实体图.

它允许使用 JPA 存储库方法,如 findById()findByName() 和动态实体图。

我更喜欢和这个助手一起使用class

public abstract class EntityGraphBuilder<T> {

    private List<String> result = new ArrayList<>();

    protected T self;

    public T add(String path) {
        result.add(path);
        return self;
    }

    public DynamicEntityGraph build() {
        return new DynamicEntityGraph(EntityGraphType.FETCH, result);
    }

}

每个实体都有自己的GraphBuilder

@Entity
public class OrderEntity {

    @Id
    private Long id;

    @Column
    private name;

    @ManyToOne(fetch = FetchType.LAZY)
    private OrderRequestEntity orderRequest;

    @ManyToOne(fetch = FetchType.LAZY)
    private ProviderEntity provider;

    public static GraphBuilder graph() {
        return new GraphBuilder();
    }

    public static class GraphBuilder extends EntityGraphBuilder<GraphBuilder> {

        private GraphBuilder() {
            self = this;
        }

        public GraphBuilder orderRequest() {
            return add("orderRequest");
        }

        public GraphBuilder provider() {
            return add("provider");
        }

    }

}

存储库使用 spring-data-jpa-entity-graph 库中的 EntityGraphJpaRepository

@Repository
public interface OrdersRepository extends EntityGraphJpaRepository<OrderEntity, Long> {

  OrderEntity findByName(String name, EntityGraph entityGraph);

}

您也可以将 findByName() 等派生查询方法用于动态实体图。

使用findById()方法的示例,同样的方法可以应用于findByName()

OrdersRepository ordersRepository;
Long orderId = 1L;

OrderEntity order = ordersRepository.findById(
  orderId,
  OrderEntity.graph().orderRequest().provider().build()
).orElseThrow(
  () -> new ServiceException("Can't find orderId=" + orderId)
);