Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use JPA - EntityGraph to load only a subset of entity @Basic attributes?

I found this documentation about entity-graphs... after read it, it gave me the idea that you can used entity-graphs to retrieve only a subset of @Basic fields of a given entity (Until now, I have used entity-graphs to retrieve relationships EAGERLY, i.e, for example, load an Employee[including all its attributes] and its associated Department[including all its attributes])...

So, I decided to try this using a small test:

@Entity
@Table(name = "employee")
@NamedEntityGraphs({
    @NamedEntityGraph(
        name = "OnlyName",
        attributeNodes = @NamedAttributeNode(value = "name")
    )
})
public class Employee implements Serializable {
    ...
    @Id
    @Column(name = "code", updatable = false)
    private Long code;

    @Basic(fetch = FetchType.LAZY)
    @Column(name = "name", nullable = false)
    private String name;

    @Basic(fetch = FetchType.LAZY)
    @Column(name = "last_name", nullable = false)
    private String lastName;

    @Lob @Basic(fetch = FetchType.LAZY)
    @Column(name = "picture", nullable = false)
    private byte[] picture;

    public Employee() {
       super();
    }
    ...
}

Then, to retrieve my entity, I used the following code:

    private Employee retrieveFromDatabase(long code) {
        EntityGraph<Employee> graph;                // Material Entity Graph

        Map<String, Object> map = new HashMap<>();

        graph = (EntityGraph<Employee>) this.em.createEntityGraph("OnlyName");
        map.put("javax.persistence.fetchgraph", graph);


        return this.em.find(Employee.class, code, map);
    }

Now, this code always returns an Employee with all the fields fetched from the database; even the Hibernate logs show a query that selects all employee fields:

Hibernate: select employee0_.code as code1_0_0_, employee0_.last_name as last_name2_0_0_, employee0_.name as name3_0_0_, employee0_.picture as picture4_0_0_ from employee employee0_ where employee0_.code=? )

When I was expecting a query like this: select employee0_.code as code1_0_0_, mployee0_.name as name3_0_0_ from employee employee0_ where employee0_.code=?

So, what I'm doing wrong? Does this feature is not supported by Hibernate??

NOTE: For the test, I was using hibernate 5.0.10 and wildfly10 ...

Thanks!

like image 889
Carlitos Way Avatar asked Nov 26 '18 23:11

Carlitos Way


People also ask

Where can we create entities classes graphs?

You can create entity graphs statically by using annotations or a deployment descriptor, or dynamically by using standard interfaces. You can use an entity graph with the EntityManager. find method or as part of a JPQL or Criteria API query by specifying the entity graph as a hint to the operation or query.

What is entity graph in spring boot?

Simply put, Entity Graphs are another way to describe a query in JPA 2.1. We can use them to formulate better-performing queries. In this tutorial, we're going to learn how to implement Entity Graphs with Spring Data JPA through a simple example.

What is the use of @entitygraph?

The @NamedEntityGraph annotation allows specifying the attributes to include when we want to load the entity and the related associations. In this example, we've used the @NamedAttributeNode to define the related entities to be loaded when the root entity is loaded.


1 Answers

Actually, you are not doing anything wrong. Yet, you are missing an important piece of information from the Hibernate ORM User Guide (v5.0.x). In section 2.3.2 we find it:

fetch - FetchType (defaults to EAGER)

Defines whether this attribute should be fetched eagerly or lazily. JPA says that EAGER is a requirement to the provider (Hibernate) that the value should be fetched when the owner is fetched, while LAZY is merely a hint that the value be fetched when the attribute is accessed. Hibernate ignores this setting for basic types unless you are using bytecode enhancement. See the BytecodeEnhancement for additional information on fetching and on bytecode enhancement.

So: Even though you are giving a query hint to the em.find(..) method, it's up to the JPA provider - here: Hibernate - to decide if basic attributes will be fetched eagerly or not. In your case, the Employee entity only contains @Basic attributes.

For reference, and for a more context as well as a more detailed explanation, see also this tutorial. It covers differences between "javax.persistence.fetchgraph" and "javax.persistence.loadgraph"query hints and has example code which demonstrates the above behaviour for the JPA persistence provider Hibernate.

Side notes: I checked..

  • .. the Hibernate ORM User Guides 5.1, 5.2, and 5.3: All versions contain the very same statement on the default policy to ignore basic attributes.
  • .. the official JPA 2.1 specification document. On (PDF) page 117, in Section 3.7.4 we find:

    The persistence provider is permitted to fetch additional entity state beyond that specified by a fetch graph or load graph.

The latter quote from the JPA-spec backs the ORM User Guide that this it is ok to implement it that way.

My advice:

Look into BytecodeEnhancement as stated by the Hibernate ORM User Guide (see above).

Hope it helps.

like image 96
MWiesner Avatar answered Sep 22 '22 04:09

MWiesner