JPA Entity Graph Example With Spring Boot

JPA 2.1 has introduced the Entity Graph feature. Applying the Entity graphs fetches the mapped entities eagerly while querying the database. We can use the JPA Entity graph feature to eliminate the N+1 select query problem while developing the spring boot jpa applications.

In this article, we will learn how to create the jpa entity graph and entity subgraph with the help of annotation and criteria API in a spring boot application.

Table of Contents

Entity graph fetch types

The Entity graphs are used to eagerly fetch the mapped entities when applied. The entity graph provides two fetch types. They are the fetchGraph and the loadGraph.

  • FetchGraph: Applying this fetch type results in fetching all the entity graph attribute nodes eagerly. Other mapped entities will be lazily fetched irrespective of the specified fetch type.
  • LoadGraph: Applying this fetch type results in fetching all the entity graph attribute nodes eagerly. Other mapped entities are treated based on the default/specified fetch type.

Based on the requirement, we can use any of the fetch types.

Creating the JPA Entity Graph with spring

We can create the entity graphs with the help of annotations or dynamically with the help of the criteria API.

Using annotations

We can add annotations to the entity class to specify the entity graph. We can also use subgraphs to apply the entity graph to multiple levels.

Let us create an entity graph with the help of annotations.

Create an entity java class with the name School.

@NamedEntityGraphs(
    value = {
        @NamedEntityGraph(
            name = "school-graph", 
            attributeNodes = { 
                @NamedAttributeNode(value = "students") 
            }
        ) 
    }
)
@Entity
@Table(name = "SCHOOL")
@Getter
@Setter
public class School {

    @Id
    @Column(name = "ID")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    @Column(name = "SCHOOL_NAME")
    private String schoolName;
    @OneToMany(mappedBy = "school", orphanRemoval = true, cascade = CascadeType.ALL)
    private List<Student> students;
}
  • @NamedEntityGraphs: If we are defining multiple entity graphs to an entity class, we can also use this annotation to club them together.
  • @NamedEntityGraph: This is the annotation that adds an entity graph for a given entity class. We can also use the name attribute to name the entity graph and attributeNodes property to specify all the attribute nodes that are part of the defined entity graph.
  • @NamedAttributeNode: This annotation adds the attribute node to the entity graph. In the above example, we have added an attribute node called students to the entity graph school-graph.

Create a java class with the name Student. This class will have many-to-one associations with the School entity.

@Getter
@Setter
@Entity
@Table(name = "STUDENT")
public class Student {

    @Id
    @Column(name = "ID")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @Column(name = "STUDENT_NAME")
    private String studentName;

    @ManyToOne
    @JoinColumn(name="SCHOOL_ID")
    private School school;
}

Now we have created the required Entity graph for the School entity class. We can fetch all the related entities along with it.

Create an interface with the name SchoolRepository and also override the spring JPA findAll() method as shown below.

public interface SchoolRepository extends JpaRepository<School, Integer> {
    @EntityGraph(type=EntityGraphType.FETCH, value="school-graph")
    public List<School> findAll();
}
  • @EntityGrpah: This annotation applies the entity graph while fetching the entities.
  • type: This attribute specifies the fetch graph type(FETCH or LOAD).
  • value: Name of the entity graph to be applied.

If we examine the generated SQL statement, we can observe that JPA is fetching all the related entities along with the parent entity even though it has the default LAZY fetch type.

select 
school0_.id as id1_0_0_, 
students1_.school_id as school_i2_1_1_, 
school0_.school_name as school_n2_0_0_, 
students1_.student_name as student_1_1_1_, 
students1_.school_id as school_i2_1_0__ 
from school school0_ 
left outer join student students1_ 
on school0_.id=students1_.school_id

We can apply the entity graph to the Spring JPA finder methods.

@EntityGraph(type=EntityGraphType.FETCH, value="school-graph")
public List<School> findBySchoolName(String name);

We can also apply the entity graph to the custom query methods.

@EntityGraph(type=EntityGraphType.FETCH, value="school-graph")
@Query(value = "select s from School s where s.schoolName =:name")
public List<School> findSchool(String name);

Both of the above methods generate the below SQL statement when executed.

select 
school0_.id as id1_0_0_, 
students1_.school_id as school_i2_1_1_, 
school0_.school_name as school_n2_0_0_, 
students1_.student_name as student_1_1_1_, 
students1_.school_id as school_i2_1_0__ 
from school school0_ 
left outer join student students1_ 
on school0_.id=students1_.school_id 
where school0_.school_name=?

Dynamically creating Entity graphs

Using the JPA criteria API, we can also dynamically apply the available entity graphs.

EntityGraph<?> entityGraph = entityManager.getEntityGraph("school-graph");
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<School> query = builder.createQuery(School.class);
Root<School> schoolRoot = query.from(School.class);
query.select(schoolRoot);
query.where(builder.equal(schoolRoot.get("schoolName"), name));
TypedQuery<School> schoolQuery = entityManager.createQuery(query);
schoolQuery.setHint("javax.persistence.fetchgraph", entityGraph);
List<School> schools = schoolQuery.getResultList();
  • We can get the defined entity graph using the entity manager’s getEntityGraph() method. Also, we can pass the entity graph name as a method parameter.
  • We can use the setHint() method to set the entity graph while fetching the entities using the JPA criteria API.

Also, we can create the entity graph dynamically using EntityManager, as shown below.

EntityGraph<?> entityGraph = entityManager.createEntityGraph("school-graph");
entityGraph.addAttributeNodes("students");
  • The EntityManager’s createEntityGraph() method creates a new entity graph dynamically.
  • We can also add attribute nodes using the addAttributeNodes() method of EntityGraph instance.

Entity sub graphs

Subgraphs allow us to build complex entity graphs with multiple levels. A subgraph is an entity graph, that is part of another entity graph or entity subgraph.

Create a java class with the name Address. A student entity class can have an address entity associated with it.

@Getter
@Setter
@Entity
@Table(name = "ADDRESS")
public class Address {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    
    @Column(name = "NAME")
    private String addressString;
}

Also, update the Student entity class by adding the address filed.

@Getter
@Setter
@Entity
@Table(name = "STUDENT")
public class Student {

    @Id
    @Column(name = "ID")
    private Integer id;

    @Column(name = "STUDENT_NAME")
    private String studentName;

    @OneToOne(fetch = FetchType.LAZY)
    @MapsId
    private Address address;

    @ManyToOne
    @JoinColumn(name="SCHOOL_ID")
    private School school;
}

Using annotation to define address sub graph

Now add the subgraph to the School entity class, as shown below.

@NamedEntityGraphs(
    value = { 
        @NamedEntityGraph(name = "school-graph", 
            attributeNodes = {
                @NamedAttributeNode(
                    value = "students", 
                    subgraph = "address-subgraph"
                ) 
            }, 
            subgraphs = {
                @NamedSubgraph(
                    name = "address-subgraph", 
                    attributeNodes = 
                    { 
                        @NamedAttributeNode("address") 
                    }
                ) 
            }
        )
    }
)
@Entity
@Table(name = "SCHOOL")
@Getter
@Setter
@ToString
public class School {
 //school entity attributes.
}
  • We can use the subgraph attribute to specify the subgraph name for a particular entity graph attribute. In the above example, we have added the address-subgraph to the entity graph.
  • We can use the subgraphs attribute of the @NamedEntityGraph annotation to specify the subgraph details.
  • @NamedSubgraph: This annotation specifies the subgraph details. The name attribute is the name of the subgraph.
  • Entity subgraphs can have attribute nodes. We can use the @NamedAttributeNode annotation to add the attributes to the subgraph.
  • In the above example, we have added the address attribute node to the address-subgraph subgraph.

Adding sub graph dynamically

EntityGraph<?> entityGraph = entityManager.createEntityGraph("school-graph");
Subgraph<?> subGraph = entityGraph.addSubgraph("students");
subGraph.addAttributeNodes("address");
		
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<School> query = builder.createQuery(School.class);
Root<School> schoolRoot = query.from(School.class);
query.select(schoolRoot);
query.where(builder.equal(schoolRoot.get("schoolName"), name));
TypedQuery<School> schoolQuery = entityManager.createQuery(query);
schoolQuery.setHint("javax.persistence.fetchgraph", entityGraph);

Below SQL query is generated by the JPA, for the above entity graph.

select 
school0_.id as id1_1_0_, 
school0_.school_name as school_n2_1_0_, 
students1_.school_id as school_i2_2_1_,  
students1_.address_id as address_3_2_1_, 
students1_.student_name as student_1_2_1_, 
students1_.school_id as school_i2_2_0__, 
address2_.id as id1_0_2_,
address2_.name as name2_0_2_ 
from school school0_ 
left outer join student students1_ 
on school0_.id=students1_.school_id 
left outer join address address2_ 
on students1_.address_id=address2_.id 
where school0_.school_name=?

Conclusion

In this article, we learned how to create the JPA entity graph and entity subgraph while developing the spring applications.

We also learned how to use annotations and JPA criteria API to generate the entity graph and subgraphs.

Using entity graphs is an option to eliminate the N+1 select query issue. But This may also result in a Cartesian product number of database record selection that again creates performance issues if the entity graph is applied to fetch multiple related entities.

JPA Entity Graph Example With Spring Boot
Scroll to top

Discover more from ASB Notebook

Subscribe now to keep reading and get access to the full archive.

Continue reading