Post

JPA 메모 — N+1, Hibernate 캐시, 직접/간접 참조, Element Collection

JPA를 다룰 때 종종 들춰보게 되는 핵심 개념 정리 — N+1 해결, 1차/2차 캐시, 직접 vs 간접 참조, Element Collection, 참조 깊이

JPA 메모 — N+1, Hibernate 캐시, 직접/간접 참조, Element Collection

JPA를 쓸 때 종종 들춰보게 되는 내용 모음.

N+1 문제

  • 연관관계 매핑 잘못으로 발생하는 대표적인 성능 문제.
  • 예: MemberTeam 일대다에서 Member 조회하면서 각자의 Team도 함께 가져오면 1번 조회 + N번 추가 쿼리.
  • 해결: fetch join, EntityGraph, batch size (@BatchSize, hibernate.default_batch_fetch_size).

직접 참조 vs 간접 참조

직접 참조 — @ManyToOne/@OneToOne 등으로 객체 그래프를 그대로 표현:

1
2
3
4
5
6
7
8
9
@Entity
public class Member {
    @Id @GeneratedValue
    private Long id;
    private String name;
    @ManyToOne
    @JoinColumn(name = "team_id")
    private Team team;
}

간접 참조 — FK 컬럼을 그냥 컬럼으로:

1
2
3
4
5
6
7
8
@Entity
public class Member {
    @Id @GeneratedValue
    private Long id;
    private String name;
    @Column(name = "team_id")
    private Long teamId;
}
방식장점단점
직접 참조객체 그래프가 명확복잡도·연관 fetch 부담
간접 참조유연, 단순, 성능 우위객체 관계가 코드에 안 드러남

DDD/MSA 문맥에서는 간접 참조가 자주 권장된다.

Hibernate Cache

1st level cache (기본 활성)

  • Session 단위.
  • 같은 세션 안에서 같은 엔티티 재조회 시 DB로 가지 않음 (영속성 컨텍스트 식별자 맵).
  • 트랜잭션 종료 시 사라짐.
1
2
3
4
Session session = sessionFactory.openSession();
Member m1 = session.get(Member.class, 1L);  // DB 쿼리
Member m2 = session.get(Member.class, 1L);  // 캐시에서 가져옴
m1 == m2;  // true (같은 객체)

2nd level cache (옵트인)

  • Session들 사이에서 공유.
  • 기본 비활성, EHCache/Redis/Infinispan 등 외부 캐시 저장소 필요.
  • @Cacheable로 엔티티 단위로 활성화.
  • Entity / Query / Collection 단위로 따로 설정 가능.

Element Collection

엔티티와 같은 생명주기를 가지는 값 컬렉션(주로 단순 값 타입) 매핑. 별도 엔티티로 빼기엔 과한 데이터일 때 사용.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Entity
public class Member {
    @Id @GeneratedValue
    private Long id;
    private String name;

    @ElementCollection
    @CollectionTable(
        name = "tbl_favorite_foods",
        joinColumns = @JoinColumn(name = "member_id")
    )
    @Column(name = "food_name")
    private Set<String> favoriteFoods = new HashSet<>();
}

대조 — @ManyToOne독립적인 생명주기를 가진 엔티티에 사용.

중첩 참조 깊이

a → b → c → d → e처럼 길게 꼬리를 무는 참조는 복잡도가 빠르게 올라간다.

가능하면 (a → c → e) + (a → b, c → d) 같은 식으로 깊이를 줄이고 형제 관계로 분해.

This post is licensed under CC BY 4.0 by the author.