Close

Understanding Entity Graphs, @NamedEntityGraph with @NamedAttributeNode Example

[Updated: Feb 8, 2018, Created: Feb 6, 2018]

What are Entity Graphs?

In JPA, entity graphs are "fetch plans" for query or find operations. A "fetch plan" specifies properties/fields (of an entity) that should be fetched either lazily or eagerly.

How to define an Entity Graph?

One of the ways to define an entity graph is to use @NamedEntityGraph annotation on the entity:

 @Entity
 @NamedEntityGraph(name = "my-graph-name", attributeNodes = @NamedAttributeNode("myProperty"))
 public class MyEntity{
    ....
    SomeType myProperty;
    ....
 }

Where @NamedAttributeNode annotation specifies the target entity's properties which should be fetched either lazily or eagerly.

How to use an Entity Graph?

After defining an entity graph, we can use it with the find method or as a query hint to override FetchType semantics. In this tutorial, we will focus on only find() method.

The standard properties javax.persistence.fetchgraph (simply referred as Fetch Graph) and javax.persistence.loadgraph (simply referred as Load Graph) are used to specify such graphs to queries and find operations.

 EntityManager em = ...
 EntityGraph graph = em.getEntityGraph("my-graph-name");
 Map<String, Object> properties = new HashMap<>();
 properties.put("javax.persistence.fetchgraph", graph);
 MyEntity myEntity = em.find(MyEntity.class, entityPrimaryKey, properties);

In above snippet we used following EntityManager.find() method:

public <T> T find(Class<T> entityClass, Object primaryKey, Map<String, Object> properties);

Fetch Graph vs Load Graph

When the javax.persistence.fetchgraph property is used, the attributes that are specified by attributeNodes are treated as FetchType.EAGER and the rest of the attributes are treated as FetchType.LAZY (regardless of whatever the default FetchType is, or specified by the developer via one of the JPA mapping annotations).

When the javax.persistence.loadgraph property is used, the attributes that are specified by attributeNodes are treated as FetchType.EAGER and the rest of the attributes are treated according to their specified or default FetchType (Original specified/default FetchType.LAZY or FetchType.EAGER are not changed).

Note that above properties (fetchgraph and loadgraph) are used as hints only and a JPA provider is permitted to ignore the lazy loading rules but it must load all entity state specified by attributeNodes:

The persistence provider is permitted to fetch additional entity state beyond that specified by a fetch graph or load graph. It is required, however, that the persistence provider fetch all state specified by the fetch or load graph.

Examples

The entities

First we are not going to use @NamedEntityGraph on our entity:

@Entity
public class User {
  @Id
  @GeneratedValue
  private int id;
  private String name;
  @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
  private Set<Address> addresses;
  @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
  private Set<Phone> phones;
    .............
}
@Entity
public class Phone {
  @Id
  @GeneratedValue
  private int id;
  private String number;
  private String type;
    .............
}
@Entity
public class Address {
  @Id
  @GeneratedValue
  private long id;
  private String street;
  private String city;
  private String country;
    .............
}

The default FetchType of @oneToMany annotation is LAZY, but we are explicitly specifying that on 'phones' field for clarity.

Using find() without an Entity Graph

public class ExampleMain {
  private static EntityManagerFactory entityManagerFactory =
          Persistence.createEntityManagerFactory("example-unit");

  public static void main(String[] args) {
      try {
          persistEntity();
          findEntity();
      } finally {
          entityManagerFactory.close();
      }
  }

  private static void findEntity() {
      EntityManager em = entityManagerFactory.createEntityManager();
      User user = em.find(User.class, 1);
      em.close();
      printInitializeStatus(user);
  }

  private static void printInitializeStatus(User user) {
      PersistenceUtil pu = entityManagerFactory.getPersistenceUnitUtil();
      System.out.println("          User loaded: " + pu.isLoaded(user));
      System.out.println("     User.name loaded: " + pu.isLoaded(user, "name"));
      System.out.println("User.addresses loaded: " + pu.isLoaded(user, "addresses"));
      System.out.println("   User.phones loaded: " + pu.isLoaded(user, "phones"));
  }

  public static void persistEntity() {
      User user = new User();
      user.setName("Jimi");
      user.addPhone("111-111-1111", "cell");
      user.addPhone("222-222-2222", "work");
      user.addAddress("111 Star Ave", "Sky Town", "Sunland");
      EntityManager em = entityManagerFactory.createEntityManager();
      em.getTransaction().begin();
      em.persist(user);
      em.getTransaction().commit();
      em.close();
  }
}
          User loaded: true
User.name loaded: true
User.addresses loaded: true
User.phones loaded: false

Defining an Entity Graph on the entity

We are going to define an entity graph for User entity targeting only 'phones' property.

@Entity
@NamedEntityGraph(name = "user-phones-entity-graph", attributeNodes = @NamedAttributeNode("phones"))
public class User {
  @Id
  @GeneratedValue
  private int id;
  @Basic(fetch = FetchType.LAZY)
  private String name;
  @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
  private Set<Address> addresses;
  @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
  private Set<Phone> phones;
    .............
}

There are no changes with Phone and Address entities.

Using Entity Graph with javax.persistence.fetchgraph

public class ExampleMain {
  private static EntityManagerFactory entityManagerFactory =
          Persistence.createEntityManagerFactory("example-unit");
    .............
  private static void findEntity() {
      EntityManager em = entityManagerFactory.createEntityManager();
      EntityGraph graph = em.getEntityGraph("user-phones-entity-graph");
      Map<String, Object> properties = new HashMap<>();
      properties.put("javax.persistence.fetchgraph", graph);
      User user = em.find(User.class, 1, properties);
      em.close();
      printInitializeStatus(user);
  }
    .............
}
          User loaded: true
User.name loaded: true
User.addresses loaded: true
User.phones loaded: true

According to the javax.persistence.fetchgraph specification, only 'phones' field should be loaded eagerly because that's the only one which is specified with attributeNodes. Hibernate ignores them (for some good reasons as discussed here). According to the specification the output should be:

          User loaded: true
     User.name loaded: false
User.addresses loaded: false
   User.phones loaded: true

Using Entity Graph with javax.persistence.loadgraph

public class ExampleMain2 {
  private static EntityManagerFactory entityManagerFactory =
          Persistence.createEntityManagerFactory("example-unit");
    .............
  private static void findEntity() {
      EntityManager em = entityManagerFactory.createEntityManager();
      EntityGraph graph = em.getEntityGraph("user-phones-entity-graph");
      Map<String, Object> properties = new HashMap<>();
      properties.put("javax.persistence.loadgraph", graph);
      User user = em.find(User.class, 1, properties);
      em.close();
      printInitializeStatus(user);
  }
    .............
}
          User loaded: true
User.name loaded: true
User.addresses loaded: true
User.phones loaded: true

Above output is per specification i.e. the specified attributeNodes should be loaded eagerly and the other fields' specified/default FetchType should be applied as they are. Note that, in above example, User.name field is equivalent to using @Basic annotation whose default FetchType is EAGER.

Example Project

Dependencies and Technologies Used:

  • h2 1.4.196: H2 Database Engine.
  • hibernate-core 5.2.12.Final: The core O/RM functionality as provided by Hibernate.
  • JDK 1.8
  • Maven 3.3.9

Entity Graph Example Select All Download
  • jpa-entity-graph-basic-example
    • src
      • main
        • java
          • com
            • logicbig
              • example
        • resources
          • META-INF

See Also