I have seen various post describing that JPA EntityGraph allows to choose the graph at run time. And I am not entirely clear what this refers to.
Out of good faith and respect I would like to take this helpful article for reference: https://www.baeldung.com/jpa-entity-graph. (Most of JPA users might have gone through it already.)
The article quotes -
EntityGraph allows grouping the related persistence fields which we want to retrieve and lets us choose the graph type at runtime.
and again solidifies above statement in conclusion section.
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.
As we see in the article (5.1) - EntityGraphs are defined as below using Annotations-
5.1. Defining an Entity Graph with Annotations
@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<>();
//...
}
The @NameEntityGraph annotation is defined at compile time and I don't see anything runtime or dynamic here.
But in 5.2 - entity graphs are defined using api or programmatically -
5.2. Defining an Entity Graph with the JPA API
EntityGraph<Post> entityGraph = entityManager.createEntityGraph(Post.class);
entityGraph.addAttributeNodes("subject");
entityGraph.addAttributeNodes("user");
In 5.2 approach, I see nodes can be chosen dynamically using some logic. So is this approach is what is refered to "dynamically fetch" and "runtime based". Or am i missing something and do i have more to understand.
Further the approaches given in 6. Using the Entity Graph
ex:
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);
are all programmatic and so can be changed during runtime i.e they can be said as dynamic.
But one approach missed in above article, but mentioned here - https://www.baeldung.com/spring-data-jpa-named-entity-graphs, as below, does not seem to fit in to dynamic criteria.
public interface ItemRepository extends JpaRepository<Item, Long> {
@EntityGraph(value = "Item.characteristics")
Item findByName(String name);
}
So does the dynamic approach just refer to 5.2 style or it implies even 5.1 style too.
CodePudding user response:
In the Baeldung article, Section 5 is only about various ways to define a graph with not much emphasis on the dynamic/non-dynamic nature of the definition itself.
In Section 5.1 the definition of the graph is static but this section is only about demonstrating how to define a graph which then will be used in typical cases when building the graph dynamically is not really necessary. This section shows an alternative to the older way of building HQL / JPA-QL queries with JOIN FETCH sections.
@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<>();
//...
}
Then, Section 6 tells you how to use the entity graphs defined earlier in various ways.
// 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);
Naturally, you can swap this first line to the fully dynamically built graph demonstrated in Section 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);
In both cases you supply an EntityGraph object to the query.
CodePudding user response:
You can't use dynamic entity graphs with spring-data, because JpaRepository
doesn't have methods to pass entity graphs like
Optional<T> findById(ID id, EntityGraph entityGraph);
You can use raw JPA for that by creating a custom repository and use entity graphs with EntityManager
.
Using pring-data-jpa-entity-graph
There is a more convenient approach by using spring-data-jpa-entity-graph.
I prefer to use it with this helper 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);
}
}
Each entity has its own GraphBuilder
@Entity
public class OrderEntity {
@Id
private Long id;
@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 provider() {
return add("provider");
}
public GraphBuilder orderRequest() {
return add("orderRequest");
}
}
}
Example of using
Long orderId = 1L;
OrderEntity order = ordersRepository.findById(
orderId,
OrderEntity.graph().orderRequest().provider().build()
).orElseThrow(
() -> new ServiceException("Can't find orderId=" orderId)
);