When JPA fetches data, there are 2 options of FetchType - EAGER and LAZY.

Intro of JPA FetchType

The default for JPA FetchType is that

  • @xxToOne - EAGER
  • @xxToMany - LAZY

FetchType is the default value of how to bring in the connected entities in 연관관계 when JPA searches for one specific entity.

  • Through ORM, we don’t have to create the queries ourselves because JPA automatically creates them for us using JPQL.
  • JPA looks at the entities and fields to create queries.
  • Thus, if the entity is mapped to other entities in 연관관계, JPA looks up at them too. We can tell JPA how to look them up through FetchType.

FetchType.EAGER

@Entity
public class Member {

    @Id @GeneratedValue
    private Long id;
    private String username;

    //default is Eager for xxToOne so no need to declare like this
    @ManyToOne(fetch = FetchType.EAGER) //Team을 조회할 때 즉시로딩을 사용하곘다!
    @JoinColumn(name = "team_id")
    Team team;
}

@Entity
public class Team {

    @Id @GeneratedValue
    private Long id;
    private String teamname;
}

Let’s do 1 search query of member with JPQL

Member findMember = em.createQuery("select m from Member m", Member.class).getSingleResult();
//멤버를 조회하는 쿼리
select
    member0_.id as id1_0_,
    member0_.team_id as team_id3_0_,
    member0_.username as username2_0_ 
from
    Member member0_

//팀을 조회하는 쿼리
select
    team0_.id as id1_3_0_,
    team0_.name as name2_3_0_ 
from
    Team team0_ 
where
    team0_.id=?

With EAGER, when it searches MEMBER, it also does a query to bring TEAM. No proxy but the actual entity.

EnumType.LAZY

Let’s change to LAZY type now.

@ManyToOne(fetch = FetchType.LAZY)

When we do the same JPQL query, we get:

//Team 조회하는 쿼리가 나가지 않음!
select
    member0_.id as id1_0_,
    member0_.team_id as team_id3_0_,
    member0_.username as username2_0_ 
from
    Member member0_

The connected entity (team) is not queried.

This is done via querying team via proxy, not the actual entity object. Don’t belive me? Let’s try this code

Member m = em.find(Member.class, member1.getId());
System.out.println(m.getTeam().getClass());

The output will be a proxy. So the only true entity (not proxy) that is queried is member.

One important side-note is that when we do something like m.getTeam().getName() where we query some fields of Team, only then will the queries for Team entity be executed. This is because DB has been 초기화ed.

Another example?

When FetchType is Lazy and we have a collection like

Member member = em.find(Member.class, member1)
List<Order> orders = member.getOrders()
Sout ordergs.getClass().getName()

Hibernate keeps track of the original collection, whilst converting it to Hibernate’s internal collection called collection wrapper. In this case, it will print

org.hibernate.collection.internal.PersistentBag

For entities in lazy setting, Hibernate uses proxy but for collections like orders, Hibernate uses collection wrapper. This collection wrapper does the role of a proxy so let’s just call it proxy.

N+1 issue

With EAGER, when we query for member, query for 연관된 객체 team is also generated. But with LAZY, the mapped entity (team) is not needed so only query for member is generated.

If member 연관된 team object is not 1 but N number of objects, wtih EAGER, when we want to search just 1 member, query for team adds up to N queries (N+1 problem)

When LAZY is not better than eager

Most of the time, LAZY should be user. But lazy is not always “better” than eager. Briefly explaining, when we do

em.find(Member.class, member1)

We get the actual member instance but we dont get the actual team object like eager. We get a team proxy instead when we do

Team team = member.getTeam()

Until we call one of Team’s methods like team.getName(), only then we will get the actual entity instead of proxy.

Ok coming back, eager is actually more efficient for entities that are connected and used commonly than lazy. So if member and team are used tgt a lot, set it as EAGER, not LAZY. This is because EAGER will use SQL outer join to search all member and team entities in 1 go, rather than separate SQL queries that Lazy will do.

The recommended way is first to put ALL 연관관계 as lazy. Then when code is almost complete, look at actual connect and how much entities are commonly used together then set those as eager. Also, for 1 entity can use Eager but for a collection, use Lazy cuz loading a collection is expensive and can load a lot of data.

One-line summary

Use LAZY !! But don’t be LAZY!