프록시와 연관관계 관리
목차
- 프록시
- 즉시 로딩과 지연로딩
- 지연 로딩 활용
- 영속성 전이: CASCADE
- 고아 객체
- 영속성 전이 + 고아 객체, 생명주기
1. 프록시
- Member와 Team 객체가 있다.
- Member와 Team 은 다대일 양방향 관계이다.
- em.find(Member.Class(), member.getId());
- 을 실행하면 Member객체와 Team 객체를 한번에 가져온다.
- Member와 Team을 거의 같이 사용한다면 문제가 되지 않지만, Member객체만 필요한 상황에서 Team 객체까지 Join하여 가져오는 것은 낭비
프록시 기초
- em.find() VS em.getReference()
- em.find(): 데이터베이스를 통해서 실제 엔티티 객체 조회
- em.getReference(): 데이터베이스 조회를 미루는 가짜(프록시) 엔티티 조회
프록시 객체의 초기화
Member member = em.getReference(Member.class, “id1”);
member.getName();
- getReference를 실해하면 member에 프록시 객체가 들어오고, 실제 member를 사용하는 getName()을 호출할때, 프록시 객체가 초기화 된다.
- 초기화란 프록시 객체가 영속성 컨텍스트에 초기화 요청을 보내고 영속성 컨텍스트가 이때 DB를 조회하여 실제 member 객체를 생성하면 프록시 객체의 target이 생성된 member객체를 가지고 있는 형식이다.
프록시 특징
- 실제 클래스를 상속받아서 만들어짐
- 따라서 타입 체크시 (==비교 실패, 대신 instance of 사용)
- 사용하는 입장에서는 진짜 객체인지 프록시 객체인지 구분하지 않고 사용
- 프록시 객체는 실제 객체의 참조(target)을 보관
- 프록시 객체를 호출(초기화)하면 프록시 객체를 실제 객체의 메소드를 호출
- 프록시 객체는 처음 사용할 때 한번 만 초기화
- 프록시 객체를 초기화 할때, 프록시 객체가 실제 엔티티로 바뀌는 것이 아님. 프록시 객체를 통해 실제 엔티티에 접근하는 것
- JPA의 트랜잭션 내부에서 같은 엔티티 반환(동일성 보장)
- 영속성 컨텍스트에 실제 엔티티가 들어가 있으면 em.getReference()를 해도 실제 엔티티 반환
- 영속성 컨텍스트에 프록시 객체가 들어가 있으면 em.find()를 해도 프록시 객체 반환
- 준영속 상태일때 프록시를 초기화하면 오류 발생
- 프록시 객체는 영속성 컨텍스트를 이용하여 초기화를 진행하기 때문
프록시 확인
- 프록시 인스턴스의 초기화 여부 확인(target)
- PersistenceUnitUtil.isLoaded(Object entity)
- 프록시 강제 초기화
- 실제 엔티티를 사용 (getName)
- Hibernate.initialize(entity); -> 하이버네이트가 지원하는 기능
- 참고: JPA 표준은 강제 초기화 기능 없음
- member.getName()을 호출해야 함
2. 즉시 로딩과 지연로딩
사실 getReference() 조회방식은 잘 사용하지 않음. 하지만 지연로딩의 방식에서 프록시 객체를 사용
지연 로딩 LAZY을 사용해서 프록시로 조회
@ManyToOne(fetch = FetchType.LAZY) //**
@JoinColumn(name = "TEAM_ID")
private Team team;
Member member = em.find(Member.class, 1L); //member객체는 DB에서 찾아옴
Team team = member.getTeam(); // 프록시 객체
team.getName(); // 실제 team을 사용하는 시점에 초기화(DB 조회)
즉시 로딩 EAGER를 사용해서 함께 조회
@ManyToOne(fetch = FetchType.EAGER) //**
@JoinColumn(name = "TEAM_ID")
private Team team;
- Member 조회시 항상 Team도 JOIN SQL로 한번에 조회
즉시 로딩 주의
- 실무에서는 지연 로딩만 사용
- 즉시 로딩을 적용하면 예상치 못한 SQL 반생
- 즉시 로딩은 JPQL에서 N + 1 문제 발생
- @ManyToOne, @OneToOne은 기본이 즉시 로딩 -> LAZY로 변경
- @OneToMany, @ManyToMany는 기본이 지연 로딩
3. 지연 로딩 활용
- 모든 연관관계에 지연 로딩적용
- 즉시 로딩이 꼭필요한 경우에는 JPQL fech조인이나, 엔티티 그래프 기능 활용
- 즉시 로딩은 상상하지 못한 쿼리가 나감
4. 영속성 전이: CASCADE
- 특정 엔티티를 영속 상태로 만들때 연관된 엔티티도 함께 영속 상태로 만들고 싶을때 사용
- ex) 부모 엔티티를 저장할때 자식 엔티티도 함께 저징
영속성 전이 - 주의
- 영속성 전이는 연관관계를 매핑하는 것과 연관이 없음
- 편리함만 제공
CASCADE 종류
- ALL: 모두 적용
- PERSIST: 영속
- REMOVE: 삭제
- MERGE: 병합
- REFRESH: REFRESH
- DETACH: DETACH
5. 고아 객체
- 부모 엔티티와 연관관계가 끊어진 자식 엔티티
orphanRemoval = true
- 고아객체 제거: 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동 삭제
Parent parent1 = em.find(Parent.class, id);
parent1.getChildren().remove(0);
//자식 엔티티를 컬렉션에서 제거
이러면 자식 Table에서도 삭제됨
주의
- 참조가 제거된 엔티티는 다른 곳에서 참조하지 않는 고아객체로 인식하여 삭제함
- 참조하는 곳이 하나일때 사용
- 특정 엔티티가 개인 소유할 때 사용
- @OneToOne, @OneToMany만 가능
- 참고: 부모를 제거하면 자식은 고아가 된다. 따라서 이 기능을 사용하면 부모를 제거하면 자식도 함께 제거된다.(CascadeType.REMOVE처럼 동작)
6. 영속성 전이 + 고아 객체, 생명주기
- CascadeType.ALL + orphanRemovel=true
- 스스로 생명주기를 관리하는 엔티티는 em.persist()로 영속화, em.remove()롤 제거
- 두 옵션을 모두 활성화 하면 부모 엔티티를 통해서 자식 엔티티의 생명주기 관리 가능
- 도메인 주도 설계(DDD)의 Aggregate Root개념을 구현할 때 유용
'JPA > JPA Basic' 카테고리의 다른 글
[JPA] 10. 객체지향 쿼리 언어2 (0) | 2023.02.04 |
---|---|
[JPA] 9. 객체지향 쿼리 언어1 (0) | 2023.02.03 |
[JPA] 8. 값 타입 (0) | 2023.01.31 |
[JPA] 6. 고급 매핑 (0) | 2023.01.29 |
[JPA] 5. 다양한 연관관계 매핑 (0) | 2023.01.29 |
[JPA] 참고. @JoinColumn (0) | 2023.01.29 |
[JPA] 참고. Entity, DAO, DTO, VO (0) | 2023.01.27 |
[JPA] 에러. SQL Error 23505 (0) | 2023.01.27 |