JPA FetchType

Sometimes you have two entities and they have a relationship between them. For example, you might have an entity called State and another entity called City where a State can have many Cities. Here the association between State and City is one-to-many.

The State entity might have some basic properties like stateId, stateName etc. Also, a State entity will contain a collection of Cities.

@Entity
public class State {
    @Id
    private long stateId;

    private String stateName;

    @OneToMany
    private Set<City> cities = new HashSet<City>();
}

When you load a State from the database, JPA loads all its basic properties like stateId, stateName. But when JPA detects the association with City or any other association, JPA looks for a configuration to decide whether to fetch the cities along with the state or defer the fetch till cities are accessed. This configuration (fetching strategy) is defined by the fetch attribute of the association.

There are two possible values for the fetch attribute –

  1. FetchType.LAZY
  2. FetchType.EAGER

If you don’t specify the fetch attribute, the default will be considered. As per JPA 2.0 specification, the defaults are –

Association Type Default fetching strategy
@OneToMany LAZY
@ManyToMany LAZY
@ManyToOne EAGER
@OneToOne EAGER

We will discuss the fetch type in detail. But before that let’s understand something called entity proxy.

Entity proxy

Let’s consider the same State and City example. Entities will look something like below.

Consider we have following data in the database –

Now try to get a State object having sateId 1 using the getReference() method of the EntityManager.

State state = entityManager.getReference(State.class, 1L);

Probably you would expect Hibernate will issue a select query to get state details. But in reality, no select query will be fired.

Hibernate just creates a State proxy that contains the identifier value (in our case 1) and reuters that. Consider proxy as a runtime-generated subclass of the corresponding entity. The proxy class name can be as weird as State$HibernateProxy$hDNQzS1L. But we don’t care about the name.

If you call getStateId() method, again no database call will happen as this method is the identifier getter method and the id value is already available to the proxy.

state.getStateId();

Now call any other method to get other field values.

state.getStateName();

This time the actual database call will happen to load the state data from the database.

select
    state0_.STATE_ID as state_id1_6_0_,
    state0_.STATE_NAME as state_na2_6_0_ 
from
    DEMO.STATE state0_ 
where
    state0_.STATE_ID = 1;

The static isLoaded() method of PersistenceUtil class can be used to check whether an entity is already initialized.

PersistenceUtil persistenceUtil = Persistence.getPersistenceUtil();
State state = entityManager.getReference(State.class, 1L);
System.out.println("Is loaded : " + persistenceUtil.isLoaded(state));
state.getStateName();
System.out.println("Is loaded : " + persistenceUtil.isLoaded(state));

Output:

Is loaded : false
Is loaded : true

So, we can conclude, when we get a proxy object, no real database call happens. When we invoke some method other than the identifier getter, the actual database call happens to load the data from the database.

FetchType.LAZY

In the case of FetchType.LAZY (lazy loading), the associated entity is loaded from the database only when they are actually needed. 

Let’s consider the same State and City example. As, fetch type is not specified for one-to-many association, it will default to LAZY.

Now, let’s try to load a State by stateId.

State state = entityManager.find(State.class, 1L);

Hibernate will generate below query to fulfill the request –

select
    state0_.STATE_ID as state_id1_6_0_,
    state0_.STATE_NAME as state_na2_6_0_ 
from
    DEMO.STATE state0_ 
where
    state0_.STATE_ID = 1;

You see, only the basic attributes of the State entity are fetched. Nothing related to City entity is selected as the fetch type is LAZY.

Now try to access cities from the state object.

Collection<City> cities = state.getCities();

As cities are requested, probably you will expect Hibernate will execute additional select query to fetch the cities. But no select query will be executed. But why?

As the association is LAZY, Hibernate doesn’t load the City collection right away. Rather Hibernate returns a special collection wrapper which can detect when you access them and load the actual data from the database at that time only.

So, try to access individual cities –

for (City city : cities) {
    System.out.println(city.getCityName());
}

Hibernate will issue a SELECT query now –

select
    cities0_.STATE_ID as state_id3_0_0_,
    cities0_.CITY_ID as city_id1_0_0_,
    cities0_.CITY_NAME as city_nam2_0_1_,
from
    DEMO.CITY cities0_ 
where
    cities0_.STATE_ID = 1;

This is about a one-to-many or many-to-many association where LAZY loading is applied. But what about one-to-one or many-to-one association where we directly refer to an entity without using collection?

@Entity
public class City {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "STATE_ID")
    private State state;
}

When you load a City entity, Hibernate loads the basic properties of the City entity like cityId, cityName etc. But for State, Hibernate will just create a proxy. So, the following statement will select the data from the CITY table, not from the STATE table.

City city = entityManager.find(City.class, 1L);
State state = city.getState(); // proxy
select
    city0_.CITY_ID as city_id1_0_0_,
    city0_.CITY_NAME as city_nam2_0_0_,
    city0_.STATE_ID as state_id3_0_0_ 
from
    DEMO.CITY city0_ 
where
    city0_.CITY_ID = 1;

When we invoke some method on the state object other than the identifier getter (getStateId), the actual database call happens to load the state data from the database.

state.getStateName();
select
    state0_.STATE_ID as state_id1_6_0_,
    state0_.STATE_NAME as state_na2_6_0_ 
from
    DEMO.STATE state0_ 
where
    state0_.STATE_ID = 1;

As we can see, using proxy and special collection wrapper, Hibernate implements lazy loading by providing just placeholders for associations which are configured to be loaded lazily.

FetchType.EAGER

If you want an association to be loaded eagerly, you can specify the fetch type as FetchType.EAGER. By default @ManyToOne and @OneToOne associations are loaded eagerly. Let’s re-use our State and City example and mark the cities to be loaded eagerly.

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<City> cities = new HashSet<City>();

Now try to load a state.

State state = entityManager.find(State.class, 1L);

This time cities will be loaded along with the State. Hibernate will issue following query to the database –

select
    state0_.STATE_ID as state_id1_6_0_,
    state0_.STATE_NAME as state_na2_6_0_,
    cities1_.STATE_ID as state_id3_0_1_,
    cities1_.CITY_ID as city_id1_0_1_,
    cities1_.CITY_ID as city_id1_0_2_,
    cities1_.CITY_NAME as city_nam2_0_2_,
    cities1_.STATE_ID as state_id3_0_2_ 
from
    DEMO.STATE state0_,
    DEMO.CITY cities1_ 
where
    state0_.STATE_ID=cities1_.STATE_ID(+) 
    and state0_.STATE_ID = 1;

You see, cities are loaded eagerly along with state.

Benefit of lazy loading

An entity can have complex structure with lots of associatiotion. If you use eager loading, along with the root entity data, Hibernate will retrieve all the association data from the database. Probably you don’t need them all initially. You will see a lot of database queries are being fired to the database to fully construct the root entity. This will definitely degrade the performance by increasing the initial loading time.

Lazy loading makes it possible to load the associations as and when they are actually needed. This will improve the initial loading time significantly.

Risk of lazy loading

With lazy loading, you might run into a LazyInitializationException if you don’t initialize the lazily fetched associations while the Session is open and you try to access that association after the Persistence Context is closed. Let’s understand this with an example. We will use the same City and State example.

@Entity
public class State {
    @OneToMany(fetch = FetchType.LAZY)
    private Set<City> cities = new HashSet<City>();
}

If you execute the following code, you will get the exception.

State state = entityManager.find(State.class, 1L);    
entityManager.close();

for (City city : state.getCities()) {
    System.out.println(city.getCityName());
}

Output:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.demo.State.cities, could not initialize proxy - no Session

In our example, first a State instance is loaded in the persistence context. But the cities are not initialized as we have used the fetch type as LAZY. Then, as we have closed the entity manager, the state instance got detached. We can initialize a proxy as long as it is in a persistent context. But as we are trying to access the cities from state after the state object is detached, we are getting the exception.

For cities to be available in detached state, we need to either load it manually while the persistence context is still open or change the fetch plan to be eager instead of lazy.

State state = entityManager.find(State.class, 1L);	
state.getCities().size();
entityManager.close();

for (City city : state.getCities()) {
	System.out.println(city.getCityName());
}

In the example above, we are calling the size method of City collection – which will initialize the collection from the database. So the collection can be accessed even if the entity manager is closed.

Another recommended approach is to use JPQL with one or more LEFT JOIN FETCH clauses. This will tell Hibernate – along with the entity, fetch all associated entities referenced in the LEFT JOIN FETCH clause.

TypedQuery<State> query = entityManager.createQuery("SELECT s FROM State s LEFT JOIN FETCH s.cities where s.stateId = :id", State.class);
query.setParameter("id", 1L);
State state = query.getSingleResult();    
entityManager.close();

for (City city : state.getCities()) {
    System.out.println(city.getCityName());
}

This will also work fine.

N+1 Select Problem

Let’s consider our City and State example. In the database we have a collection of States represented by individual rows in the STATE table. Against individual States, we have multiple rows in the CITY table as the relationship between State and city is one-to-many.

Now consider, we have to iterate through all the states and and for each state, we have to display the city names. To achieve this you may write something like below –

TypedQuery<State> query = entityManager.createQuery("SELECT s FROM State s", State.class);
List<State> states = query.getResultList();

for (State state : states) {
    System.out.println("State : " + state.getStateName());
    for (City city : state.getCities()) {
        System.out.println("City : " + city.getCityName());
    }
}

This will print the correct result. But internally Hibernate will first issue a query to get all state data.

select STATE_ID, STATE_NAME from DEMO.STATE;

Then for each state, Hibernate will issue individual select queries to get all cities against that state.

select CITY_ID, CITY_NAME, STATE_ID from DEMO.CITY where STATE_ID = ?;

So, if you have N number of states in the database, there will be 1 select query for state and then N additional selects for City. This is called the N+1 select problem.

If the value of N is large, a large number of additional select queries will be executed to select data from the CITY table. These larger round-trips to the database will consume a sufficient amount of time and eventually degrade the performance.

You may think, if you change the fetch type to EAGER from LAZY, that will solve this problem. But that is not true. Fetch type will only indicate when those additional queries will be executed, early or when required. This will not reduce the query count.

To solve this N+1 select problem, you should fetch all state and city data at once using JOIN FETCH.

TypedQuery<State> query = entityManager.createQuery("SELECT s FROM State s LEFT JOIN FETCH s.cities", State.class);
List<State> states = query.getResultList();

for (State state : states) {
    System.out.println("State : " + state.getStateName());
    for (City city : state.getCities()) {
        System.out.println("City : " + city.getCityName());
    }
}

This will issue only one select query and reduces the number of round-trips to the database from N+1 to 1.

select
    state0_.STATE_ID as state_id1_6_0_,
    state0_.STATE_NAME as state_na2_6_0_,
    cities1_.CITY_ID as city_id1_0_1_,
    cities1_.CITY_NAME as city_nam2_0_1_,
    cities1_.STATE_ID as state_id3_0_1_
from DEMO.STATE state0_ 
left outer join DEMO.CITY cities1_ 
on state0_.STATE_ID=cities1_.STATE_ID;

That’s it for now. Hope you have enjoyed this tutorial. If you have any doubt, please ask in the comment section. I will try to answer that as soon as possible. Till then, bye bye.