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.
Advertisement
Answer
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);
Using custom JPA repository
You can use raw JPA for that, by creating a custom repository and using entity graphs with EntityManager
.
Using spring-data-jpa-entity-graph
There is a more convenient approach by using library spring-data-jpa-entity-graph.
It allows to use JPA repository methods like findById()
or findByName()
with dynamic entity graphs.
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; @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"); } } }
Repository uses EntityGraphJpaRepository
from spring-data-jpa-entity-graph
library
@Repository public interface OrdersRepository extends EntityGraphJpaRepository<OrderEntity, Long> { OrderEntity findByName(String name, EntityGraph entityGraph); }
You can use derived query methods like findByName()
with dynamic entity graphs too.
Example of using findById()
method, the same approach can be applied to findByName()
OrdersRepository ordersRepository; Long orderId = 1L; OrderEntity order = ordersRepository.findById( orderId, OrderEntity.graph().orderRequest().provider().build() ).orElseThrow( () -> new ServiceException("Can't find orderId=" + orderId) );