Follow @vlad_mihalcea Imagine having a tool that can automatically detect if you are using JPA and Hibernate properly. Hypersistence Optimizer is that tool!

Introduction

One of my readers bumped into the JSON mapping post and asked me if we can fetch the JSON properties lazily. This post demonstrates how easily this can be done when using Hibernate as a JPA provider.

As I previously explained, EAGER fetching is a code smell and loading associations eagerly is very detriment to application performance. However, it’s not just associations that we must be careful about. Basic entity properties may also cause performance issues as well. In this post, I’m going to show you how you can fetch entity properties lazily.

Bytecode enhancement

By default, Hibernate loads all properties eagerly. So if your entity has many properties or the underlying columns are relatively large, there is going to be a considerable performance impact upon fetching the entity in question. While associations can be loaded lazily using runtime-generated proxies, for basic properties we need bytecode enhancement.

To enhance our entities with basic property lazy loading capabilities, the enableLazyInitialization configuration property must be explicitly set to true :

<plugin> <groupId>org.hibernate.orm.tooling</groupId> <artifactId>hibernate-enhance-maven-plugin</artifactId> <version>${hibernate.version}</version> <executions> <execution> <configuration> <enableLazyInitialization>true</enableLazyInitialization> </configuration> <goals> <goal>enhance</goal> </goals> </execution> </executions> </plugin>

Domain Model

I’m going to reuse the same Domain Model used in the JSON mapping post. Basically, we want to fetch all the JSON properties lazily because the JSON object size might be quite large.

The Event entity has a location property that stores a JSON object. To load it lazily, we need to mark this property with the @Basic(fetch = FetchType.LAZY) annotation:

@Entity(name = "Event") @Table(name = "event") public class Event extends BaseEntity { @Type(type = "jsonb") @Column(columnDefinition = "jsonb") @Basic(fetch = FetchType.LAZY) private Location location; public Location getLocation() { return location; } public void setLocation(Location location) { this.location = location; } }

The Participant entity has a ticket property that stores a JSON object, therefore this property is annotated with the with @Basic(fetch = FetchType.LAZY) as well:

@Entity(name = "Participant") @Table(name = "participant") public class Participant extends BaseEntity { @Type(type = "jsonb") @Column(columnDefinition = "jsonb") @Basic(fetch = FetchType.LAZY) private Ticket ticket; @ManyToOne(fetch = FetchType.LAZY) private Event event; public Ticket getTicket() { return ticket; } public void setTicket(Ticket ticket) { this.ticket = ticket; } public Event getEvent() { return event; } public void setEvent(Event event) { this.event = event; } }

Testing time

To prove that property lazy loading works, we are going to fetch the Event entity and navigate the location property as follows:

Event event = entityManager.find(Event.class, eventHolder.get().getId()); LOGGER.debug("Fetched event"); assertEquals("Cluj-Napoca", event.getLocation().getCity());

When running the test case above, Hibernate generates the following statements:

SELECT e.id AS id1_0_0_ FROM event e WHERE e.id = 1 -- Fetched event SELECT e.location AS location2_0_ FROM event e WHERE e.id = 1

As you can see, the first query does not retrieve the location property, which is only initialized when being navigated for the first time.

The same goes for our Participant entity:

Participant participant = entityManager.find(Participant.class, participantHolder.get().getId()); LOGGER.debug("Fetched participant"); assertEquals("ABC123", participant.getTicket().getRegistrationCode());

Hibernate generating the following SQL statements:

SELECT p.id AS id1_1_0_ , p.event_id AS event_id3_1_0_ FROM participant p WHERE p.id = 1 -- Fetched participant SELECT p.ticket AS ticket2_1_ FROM participant p WHERE p.id = 1

Conclusion

Fetching entity properties lazily comes in handy whenever we deal with BLOB , CLOB , BINARY , TEXT , and JSON column types. By default, all lazy properties are initialized together, but this behavior can be customized using @LazyGroup , as it will be explained in a future post.

Insert details about how the information is going to be processed DOWNLOAD NOW