Entity 조회
📌 em.getReference()는 JPA의 EntityManager에서 제공하는 메서드로 특정 엔티티의 프록시 객체를 반환한다. 지연 로딩(Lazy Loading)을 활용해 데이터베이스 조회를 미루고 실제로 엔티티의 속성에 접근할 때만 데이터베이스를 조회하도록 한다.
@Entity
@Table(name = "tutor")
public class Tutor {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "company_id")
private Company company;
public Tutor() {
}
public Tutor(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
}
// 1. tutor의 company를 함께 조회하는 경우
Tutor findTutor = em.find(Tutor.class, 1L);
String tutorName = findTutor.getName();
Company tutorCompany = findTutor.getCompany();
System.out.println("tutorName = " + tutorName);
System.out.println("tutorCompany.getName() = " + tutorCompany.getName());
// 2. tutor만 조회하는 경우
Tutor findTutor = em.find(Tutor.class, 1L);
String tutorName = findTutor.getName();
System.out.println("tutorName = " + tutorName);
- Tutor를 조회할 때 Company 를 함께 조회
- Company를 매번 함께 조회하는것은 낭비이다.
- Tutor만 조회
- Company 조회를 위해 추가적인 조회 SQL이 실행되어야 한다.
- 이때 프록시를 사용하여 효율적으로 관리할 수 있다.
Proxy
📌 JPA에서 엔티티 객체의 지연 로딩(Lazy Loading)을 지원하기 위해 사용하는 대리 객체로 실제 엔티티 객체를 생성하거나 데이터베이스에서 값을 읽어오지 않고도 엔티티의 참조를 사용할 수 있다.
- 대리자 또는 중간 대리 객체를 의미
- 데이터베이스 조회를 지연하는 가짜(Proxy) 객체를 조회한다.
- 실제 Entity와 == 비교 실패, instanceof 사용
- target : 진짜 객체의 참조를 보관한다.
Proxy 객체 초기화
- em.getReference() : 프록시 객체 조회
- getName() : 프록시 객체의 getName() 호출
- JPA가 영속성 컨텍스트에 target 초기화 요청
- 실제 DB 조회
- Entity 생성
- target의 getName() 호출
Proxy 특징
- 최초로 사용(실제 Entity에 접근)할 때 한 번만 초기화된다.
- 프록시 객체를 통해 실제 Entity에 접근할 수 있다.
- em.getReference() 호출 시 영속성 컨텍스트에 Entity가 존재하면 실제 Entity가 반환된다.
- 준영속 상태에서 프록시를 초기화하면 LazyInitializationException 예외가 발생한다.
Tutor proxyTutor = em.getReference(Tutor.class, tutor.getId());
System.out.println("proxyTutor.getClass() = " + proxyTutor.getClass());
// 준영속 상태
em.detach(proxyTutor);
proxyTutor.getName();
실행결과
- detach() : 영속성 컨텍스트가 관리하지 않는다.
- 영속성 컨텍스트를 통해 도움을 받아야만 실제 Entity에 접근이 가능하다.
- 실제 JPA 개발에서 가장 많이 마주치는 Exception
영속성 컨텍스트 (Persistence Context)
영속성 컨텍스트는 JPA(Java Persistence API)에서 엔티티 객체를 **영구 저장소(데이터베이스)**에 저장하거나 관리하는 중간 작업 영역을 의미합니다. 쉽게 말해, 엔티티 객체를 관리하는 JPA의 메모리 공간입니다.
영속성 컨텍스트의 주요 특징
- 엔티티 관리:
- 영속성 컨텍스트는 엔티티 객체를 영속성 상태로 관리합니다.
- 관리 중인 엔티티는 변경 사항이 자동으로 데이터베이스에 반영됩니다.
- 1차 캐시:
- 영속성 컨텍스트는 엔티티를 1차 캐시에 저장하여, 동일한 엔티티를 데이터베이스에서 다시 조회하지 않도록 최적화합니다.
- 엔티티 동일성 보장:
- 동일한 영속성 컨텍스트 내에서는 동일한 엔티티 객체를 공유(동일성 보장)합니다.
- 변경 감지:
- 영속성 컨텍스트는 관리 중인 엔티티의 변경 사항을 감지하여 데이터베이스에 자동으로 반영합니다.
- 쓰기 지연:
- 트랜잭션 커밋 시점에 변경 사항을 한꺼번에 데이터베이스에 반영하여 성능을 최적화합니다.
- 지연 로딩:
- 필요한 시점에만 데이터베이스에서 데이터를 로드하여 효율적으로 자원을 사용합니다.
영속성 컨텍스트의 상태
- 비영속 (Transient):
- 영속성 컨텍스트에서 관리되지 않는 상태.
- 데이터베이스와 전혀 관련이 없는 상태.
User user = new User(); // 비영속 상태 user.setName("John");
- 영속 (Persistent):
- 영속성 컨텍스트에 의해 관리되는 상태.
- 데이터베이스와 연동되며 변경 사항이 자동으로 반영됩니다.
User user = new User(); user.setName("John"); entityManager.persist(user); // 영속 상태
- 준영속 (Detached):
- 영속성 컨텍스트에서 관리되지 않지만, 이전에 영속 상태였던 엔티티.
- 데이터베이스와 연동되지 않습니다.
entityManager.detach(user); // 준영속 상태
- 삭제 (Removed):
- 삭제 요청이 되어 데이터베이스에서 삭제될 예정인 상태.
- 트랜잭션이 커밋되면 데이터베이스에서 삭제됩니다.
entityManager.remove(user); // 삭제 상태
영속성 컨텍스트의 주요 기능
1. 1차 캐시
- 영속성 컨텍스트는 엔티티를 1차 캐시에 저장합니다.
- 동일한 엔티티를 반복적으로 조회할 경우, 데이터베이스 대신 1차 캐시에서 데이터를 가져옵니다.
User user1 = entityManager.find(User.class, 1L); // DB 조회
User user2 = entityManager.find(User.class, 1L); // 1차 캐시에서 조회
2. 엔티티 동일성 보장
- 동일한 영속성 컨텍스트에서는 동일한 엔티티 객체를 반환합니다.
User user1 = entityManager.find(User.class, 1L);
User user2 = entityManager.find(User.class, 1L);
System.out.println(user1 == user2); // true (같은 객체)
3. 변경 감지 (Dirty Checking)
- 엔티티의 필드 값이 변경되면 영속성 컨텍스트가 이를 감지하고, 트랜잭션 커밋 시점에 데이터베이스에 반영합니다.
User user = entityManager.find(User.class, 1L);
user.setName("Updated Name"); // 변경 감지
entityManager.flush(); // 변경 사항 반영
4. 쓰기 지연 (Write-Behind)
- 데이터베이스에 변경 사항을 즉시 반영하지 않고, 트랜잭션 커밋 시점에 한꺼번에 반영합니다.
entityManager.persist(user1);
entityManager.persist(user2);
// SQL 실행은 트랜잭션 커밋 시점에 발생
transaction.commit();
5. 지연 로딩 (Lazy Loading)
- 연관된 엔티티를 실제로 사용할 때까지 데이터베이스에서 로드하지 않습니다.
User user = entityManager.find(User.class, 1L);
List<Order> orders = user.getOrders(); // 이 시점에 DB에서 로드
영속성 컨텍스트의 동작 예시
엔티티 상태 전환
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
}
// 비영속 상태
User user = new User();
user.setName("John");
// 영속 상태
entityManager.persist(user);
// 준영속 상태
entityManager.detach(user);
// 삭제 상태
entityManager.remove(user);
트랜잭션과 영속성 컨텍스트
- 영속성 컨텍스트는 트랜잭션 범위 내에서 관리됩니다.
- 트랜잭션이 종료되면 영속성 컨텍스트도 종료됩니다.
- 트랜잭션이 커밋되면 데이터베이스와 동기화됩니다.
- 엔티티의 변경 사항이 데이터베이스에 반영됩니다.
영속성 컨텍스트의 장점
- 성능 최적화:
- 1차 캐시와 쓰기 지연을 통해 데이터베이스 접근 횟수를 줄임.
- 변경 감지:
- 엔티티의 변경 사항을 자동으로 반영하여 코드 단순화.
- 지연 로딩:
- 필요한 시점에만 데이터를 로드하여 리소스 사용을 최소화.
- 엔티티 동일성 보장:
- 동일한 트랜잭션 내에서 같은 데이터를 일관되게 처리 가능.
영속성 컨텍스트를 활용한 주요 작업 흐름
- 엔티티 생성 및 영속화:
- 새 엔티티 객체를 생성하고, 영속성 컨텍스트에 추가.
- 엔티티 조회:
- 데이터베이스에서 데이터를 가져와 영속성 컨텍스트에 저장.
- 변경 사항 감지:
- 엔티티의 필드 값이 변경되면 자동으로 감지하여 반영.
- 엔티티 삭제:
- 영속성 컨텍스트에서 엔티티를 제거하고, 데이터베이스에서도 삭제.
정리
영속성 컨텍스트는 JPA가 엔티티 객체를 관리하고, 데이터베이스와의 상호작용을 효율적으로 처리하기 위해 사용하는 핵심 메커니즘입니다.
- 핵심 기능: 1차 캐시, 변경 감지, 쓰기 지연, 지연 로딩.
- 장점: 성능 최적화, 코드 단순화, 데이터 일관성 보장.
쉽게 말해, 영속성 컨텍스트는 JPA가 엔티티 객체를 "관리"하고 "동기화"하는 공간입니다!
'DB 접근 > JPA ( Java Persistence API )' 카테고리의 다른 글
JPA와 Transaction (0) | 2025.01.17 |
---|---|
지연로딩, 즉시로딩 (0) | 2025.01.15 |
상속관계 매핑 (0) | 2025.01.13 |
연관관계 (0) | 2025.01.12 |
[JPA] Spring Data JPA (0) | 2025.01.07 |