영속성 컨텍스트의 특징과 프록시 이해하기
이번 글에는 JPA의 필수 개념인 영속성 컨텍스트와 프록시에 대해 알아보겠습니다. JPA를 사용할 때 이 개념들을 모른다면 실무에서 JPA를 사용할 때 다양한 오류들을 만나게 되며 성능 부분에서 안 쓰는게 나을 정도로 심각한 문제를 야기할 수 있습니다. 그럼 개념에 대해 알아봅시다.
영속성 컨텍스트란?
영속성 컨텍스트란 엔티티를 영구 저장하는 환경을 뜻합니다. JPA는 테이블과 매핑되는 엔티티 객체 정보를 영속성 컨텍스트를 통해 애플리케이션 내에서 보관합니다. 엔티티를 저장하거나 조회하면 영속성 컨텍스트에 보관하고 관리하게 됩니다. 또한 아래와 같은 상태를 가집니다.
- 비영속(new/transient) : 영속성 컨텍스트와 전혀 관계가 없는 상태
- 영속(managed): 영속성 컨텍스트에 저장되어 관리되는 상태
- 준영속(detached): 영속성 컨텍스트에 저장되었다가 분리된 상태
- 삭제(remove): 삭제된 상태
영속성 상태의 특징
- 1차 캐시 - 영속 상태의 엔티티를 저장하기 때문에 같은 엔티티를 여러 번 로드할 때 캐싱하여 성능을 최적화합니다.
Member member = new Member();
member.setId(1L);
member.setName("user_1");
em.persist(member);
// 영속성 컨텍스트의 1차 캐시에서 조회
Member findMember = em.find(Member.class, 1);
- 동일성 보장 - 같은 트랜잭션 내에서 같은 객체를 반환하게 되면 새로 데이터베이스에 접근해서 데이터를 가져오지 않고 동일한 객체를 반환합니다.
Member first = em.find(Member.class, 1L);
Member second = em.find(Member.class, 1L);
System.out.println(first == second); // 동일성을 보장
- 쓰기 지연 - 트랜잭션 내에서 엔티티를 관리하며, 트랜잭션이 완료될 때 변경 사항을 데이터베이스에 반영합니다.
em.persist(first);
em.persist(second);
//커밋되는 시점에 반영
tx.commit();
- 변경 감지 - 엔티티를 조회해서 엔티티를 수정할 때, 따로 변경 쿼리를 날리지 않더라도 이전에 불러온 영속성컨텍스트 내의 스냅샷과 엔티티를 비교하여 수정된 부분에 대한 변경 쿼리를 실행합니다.
Member first = em.find(Member.class, 1L);
first.setName("user_2");
//스냅샷 비교 후 Update 실행
tx.commit();
- 지연 로딩 - 지연 로딩은 연관관계 매핑되어있는 엔티티 조회시 프록시 객체를 반환하고, 실제 사용될 때 쿼리를 날려 가져오는 기능입니다.
프록시란?
프록시란 '대리', '대신'이라는 뜻을 가지며, 프로토콜에 있어서는 대리 응답 등에서 사용하는 개념입니다.
JPA에서 엔티티를 조회할떄는 엔티티 매니저를 사용하는데, 영속성 컨텍스트에 엔티티가 없으면 실제 DB를 조회하는 메서드입니다.
Hibernate에서 내부적으로 상속받아 만들어지며 실제 클래스와 겉 모양이 같은 형태로 존재합니다.
프록시 객체는 실제 객체의 참조 값을 보관하고 객체를 사용할 때 실제 조회를 하게 됩니다.
프록시와 지연로딩
Member와 Team이 N:1 관계일 때 Member을 조회하면 Team의 참조 값을 보관하여 실제 사용할 때 쿼리를 날리게 됩니다.
Member member = em.find(Member.class, 1L);
Team team = em.find(Team.class, 1L);
member.setTeam(team);
em.persist(member);
em.flush();
em.clear();
Member findMember = em.find(Member.class, 1L);
//지연 로딩
member.getTeam().getName();
- 프록시 객체에서 member.getTeam().getName()을 호출해서 데이터를 조회합니다.
- 실제 엔티티가 생성되어 있지 않으면 영속성 컨텍스트에서 엔티티를 생성하며 이러한 과정을 초기화라고 합니다.
- 이 경우 영속성 컨텍스트는 데이터베이스를 조회해서 참조 값을 기준으로 엔티티 객체를 생성합니다.
- 프록시 객체는 해당 엔티티의 참조 값을 기준으로 데이터를 보관하고, 이를 통해 조회 시 값을 나타낼 수 있습니다.
이번 글에서는 영속성 컨텍스트와 프록시, 그리고 그 둘 사이의 관계에 대해 알아보았습니다. 이를 통해 JPA를 사용할 때 한 트랜잭션 내에서 어떤 방식으로 엔티티가 관리되는지, 객체 그래프의 탐색 시에는 영속성 컨텍스트와 프록시가 어떻게 적용되는지 한번 더 생각해 보는 시간을 갖게 된 것 같습니다. 다음 글에는 영속성 컨텍스트와 프록시의 이해를 참고로 하여 JPA의 연관관계와 지연 로딩의 주의할 점인 N+1 문제에 대해 알아보겠습니다. 읽어주셔서 감사합니다.
'Spring' 카테고리의 다른 글
[JPA] QueryDSL에서 JPA N+1 문제 해결하기 (0) | 2024.02.13 |
---|---|
[JPA] JPA의 연관관계 예제 정리 (1) | 2024.02.05 |
[JPA] JPA 개념과 기본 설정 가이드 (0) | 2024.01.29 |
[Spring] SpringBoot JWT 로그인 구현 가이드 (1) | 2024.01.26 |
[Spring] SpringBoot Swagger 연동 (3.x ver) (0) | 2024.01.10 |