인프런 - 자바 ORM 표준 JPA 프로그래밍 (김영한님)
프록시
- em.find() vs em.getReference()
- em.find() : 데이터베이스를 통해 실제 엔티티 객체 조회
- em.getReference() : 데이터베이스 조회가 아닌 가짜(프록시) 엔티티 객체 조회
Member member = new Member();
member.setName("test1");
em.persist(member);
em.flush();
em.clear();
// findMember = Proxy 클래스
Member findMember = em.getReference(Member.class, member.getId());
// member.getId()는 이미 알고 있는 값이므로 DB 조회하지 않음
System.out.println(findMember.getId());
프록시 특징
- 실제 클래스를 상속 받아서 만들어짐
- 실제 클래스와 겉 모양이 같음
- 프록시 객체는 실제 객체의 참조(target)을 보관
- 프록시 객체를 호출하면 프록시 객체는 실제 객체의 메소드를 호출
프록시 객체의 초기화
Member member = em.getReference(Member.class, "id1");
member.getName();
- member는 프록시 객체
- member.getName() 호출 시 MemberProxy의 target은 null 상태
- 영속성 컨텍스트에 초기화 요청
- DB에서 Member를 조회해 반환
- MemberProxy의 target에 조회된 실제 Entity를 연결 후 target.getName()으로 반환
프록시의 특징
- 프록시 객체는 처음 사용할 때 한번만 초기화
- 프록시 객체를 초기화 할 때, 프록시 객체가 실제 엔티티로 바뀌는 것은 아님
- 초기화 되면 프록시 객체를 통해 실제 엔티티에 접근 가능
- 프록시 객체는 원본 엔티티를 상속 받음
- 타입 체크시 주의 (== 비교 대신 instance of 사용)
- 영속성 컨텍스트에 찾는 엔티티가 이미 있으면 em.getReference()를 호출해도 실제 엔티티를 반환
- 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화하면 문제 발생
- LazyInitializationException 발생
영속성 컨텍스트에 찾는 엔티티가 이미 있으면 em.getReference()를 호출해도 실제 엔티티를 반환
Member m1 = em.find(Member.class, member1.getId());
System.out.println(m1.getClass());
// class hellojpa.Member
Member reference = em.getReference(Member.class, member1.getId());
System.out.println(reference.getClass));
// class hellojpa.Member
/**
* em.find 했을 때 영속성 컨텍스트에 올라간 상태
* JPA에서는 한 영속성 컨텍스트에서 가져온 것은 == 비교를 했을 때 true
*/
Member ref1 = em.getReference(Member.class, member1.getId());
Member ref2 = em.getReference(Member.class, member1.getId());
System.out.println(ref1 == ref2)
// true
Member ref1 = em.getReference(Member.class, member1.getId());
Member m1 = em.find(Member.class, member1.getId());
System.out.println(ref1 == m1);
// true
/**
* m1은 Proxy 객체가 반환되므로 true
*/
프록시 확인
- 프록시 인스턴스의 초기화 여부 확인
PersistenceUnitUtil.isLoaded(Object entity);
- 프록시 클래스 확인 방법
entity.getClass().getName();
- 프록시 강제 초기화
org.hibernate.Hibernate.initialize(entity);
- 참고: JPA 표준은 강제 초기화 없음
- 강제 호출 : member.getName();
즉시 로딩과 지연 로딩
지연 로딩
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name "TEAM_ID")
private Team team;
}
Member member = em.find(Member.class, 1L);
// team은 프록시 객체
Team team = member.getTeam();
// 실제 team을 조회하는 시점에 초기화
team.getName();
- FetchType.LAZY : Member 클래스만 조회, Team은 프록시로 조회
- member.getTeam(); 까지는 프록시 객체만 가져오므로 조회 쿼리가 나가지 않음
- team.getName(); 으로 실제 team을 조회 시 DB에서 조회
즉시로딩
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
private String name;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name "TEAM_ID")
private Team team;
}
// member 조회 시 이미 Team도 조회됨
Member member = em.find(Member.class, 1L);
Team team = member.getTeam();
team.getName();
- Member와 Team을 자주 함께 사용한다면 즉시 로딩 사용
- JPA 구현체는 가능하면 조인을 사용해 SQL 한번에 함께 조회
프록시와 즉시 로딩 주의
- 가급적 지연 로딩만 사용 (특히 실무에서)
- 즉시 로딩을 적용하면 예상하지 못한 SQL이 발생할 수 있음
- 즉시 로딩은 JPQL에서 N+1 문제가 발생
- 1을 조회하기 위해 N개의 쿼리가 추가 실행되는 문제
- @ManyToOne, @OneToOne은 기본이 즉시 로딩이므로 LAZY로 설정 필요!!
- @OneToMany, @ManyToMany는 기본이 지연 로딩
영속성 전이 : CASCADE
- 특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속 상태로 만들고 싶을 때
- 즉시로딩, 지연로딩과 관계 X
- 엔티티를 영속화할 때 연관된 엔티티도 함께 영속화하는 편리함을 제공할 뿐 그 이상도 이하도 아님
- 예 ) 부모 엔티티를 저장할 때 자식 엔티티도 함께 저장
CASCADE 종류
- ALL: 모두 적용
- PERSIST: 영속(저장할 때만)
- REMOVE: 삭제
@OneToMany(mappedBy="parent", cascade=CascadeType.ALL)
고아객체
- 고아객체 제거 : 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제
- orphanRemoval = true로 설정했을 떄
Parent parent = em.find(Parent.class, parent.getId());
// remove(0)을 수행하면 orphanRemoval가 실행됨
parent.getChildList().remove(0);
고아객체 주의
- 참조하는 곳이 하나일 때 사용
- 특정 엔티티가 개인 소유할 때 사용
- 즉, @OneToOne, @OneToMany만 가능
영속성 전이 + 고아 객체, 생명주기
- CascadeType.ALL + orphanRemovel = true, 두 옵션을 모두 활성화 하면 부모 엔티티를 통해 자식의 생명 주기를 관리할 수 있음
- 자식의 Repository를 만들지 않아도 됨
- 도메인 주도 설계(DDD)의 Aggregate Root 개념을 구현할 때 유용
'STUDY > JPA' 카테고리의 다른 글
[자바 ORM 표준 JPA 프로그래밍 - 기본편] 객체지향쿼리언어1 - 기본문법 (1) | 2023.12.10 |
---|---|
[자바 ORM 표준 JPA 프로그래밍 - 기본편] 값 타입 (0) | 2023.12.10 |
[자바 ORM 표준 JPA 프로그래밍 - 기본편] 고급 매핑 (0) | 2023.12.10 |
[자바 ORM 표준 JPA 프로그래밍 - 기본편] 다양한 연관관계 매핑 (1) | 2023.12.10 |
[자바 ORM 표준 JPA 프로그래밍 - 기본편] 연관관계 매핑 기초 (0) | 2023.12.10 |