프록시와 연관관계 관리
프록시
이런 엔티티가 있다고 해보자
public void printMemberName(String memberId){
Member member = em.find(Member.class, memberId);
System.out.println("회원 이름: "+member.getUsername());
}
Member에서 username을 가져올때 Team의 객체까지 가지고와서 회원이름이 출력이된다. 이런 방식은 매우 효율적이지 않은 방식이다. 하지만 JPA는 엔티티가 실제 사용될 때까지 데이터베이스 조회를 지연시키는 지연 로딩을 제공한다.
그럼 지연 로딩을 사용하려면 실제 엔티티 대신에 가짜 객체가 필요한데 가짜 객체를 프록시 객체라고한다.
프록시 기초
Member member = em.find(Member.class, memberId);
em.find를 사용하게 되면 실제 객체에 접근해서 조회하게한다.
Member member = em.getReference(Member.class, memberId);
em.getReference()를 사용하면 실제 엔티티를 사용하는 시점에 조회가된다.
이메소드는 실제 객체를 넘기는게 아닌 실게 객체와 똑같이 만들어진 프록시 객체를 넘기게된다.
사진과 같이 엔티티와 똑같은 형태를 가진 프록시 객체가 생성되며 실제 객체에서 위임(delegate)를 해주게된다.
프록시 특징
- 처음 한번만 초기화된다.
- 프록시객체가 생성된다고해서 실제 객체가 바뀌는것은아니다.
- 프록시 객체는 실제 객체를 통해 초기화되는것이니. 타입 체크를 주의해야한다.
- 영속성 컨텍스트에 찾는 엔티티가 있으면 프록시 객체가 반환되지않고 실제 객체가 반환된다.
- 초기화는 영속성 컨텍스트의 도움을 받아야가능하다. 만약 도움을 받을수 없는 준영속 상태의 프록시를 초기화하면 문제가 발생한다.
프록시 확인
boolean isLoad = em.getEntityManagerFactory()
.getPersistenceUnitUtil().isLoaded(entity);
System.out.println("isLoad= "+isLoad);
코드를 사용하면 인스턴스가 초기화 된 프록시 인스턴스는 false를 반환하며, 이미초기화되었거나 프록시 인스턴스가 아니라면 true를 반환한다.
즉시 로딩과 지연 로딩
위에 나왔던 Member 엔티티와 Team 엔티티가 있고, printMemberName() 메소드를 실행한다고 생각해보자.
public void printMemberName(String memberId){
Member member = em.find(Member.class, memberId);
Team team = member.getTeam();
System.out.println("회원 이름: "+member.getUsername());
}
즉시 로딩
@Entity
@Data
public class Member {
@Id
private Long id;
private String username;
@ManyToOne(fetch = FetchType.EAGER)
private Team team;
}
즉시로딩 사용방법은 매핑옵션에(fetch= FetchType.EAGER)을 적용함면된다. 즉시로딩은 em.find()로 조회시에 연관된 모든 엔티티를 모두 로딩하는 방식으로 사용된다 그러면 조회쿼리도 두번 호출되는것 같지만 최적화를 위해서 조인 쿼리를 사용한다.
@ManyToOne 에서는 Default 값으로 EAGER이 설정되어있다.
지연 로딩
@Entity
@Data
public class Member {
@Id
private Long id;
private String username;
@ManyToOne(fetch = FetchType.LAZY)
private Team team;
}
지연 로딩 사용방법은 fetchType.LAZY로 설정하면된다. 지연 로딩을 사용하게되면 사용하지 않는 객체는 프록시 객체로 만들어 저장하고 실제 객체가 사용된다면 프록시를 통해서 실제 객체를 조회하게된다.
만약 영속성 컨텍스트에 team 객체가 있다면 프록시 객체를 사용하지않고 실제 객체를 사용한다.
영속성 전이: CASCADE
다음과 같이 Item 객체를 영속상태를 만들때 연관된 객체까지 영속 상태를 만드려먼 CASCADE옵션을 사용하면된다.
Item
@Entity
@Data
public class Item {
@Id
private Long id;
private String ItemName;
@OneToMany(mappedBy = "item", cascade = CascadeType.PERSIST)
private List<Book> books = new ArrayList<>();
}
Book
@Entity
@Data
public class Book {
@Id
private Long id;
private String isbn;
@ManyToOne
private Item item;
}
이렇게 CASCADE 옵션을사용하면 편리하게 연관된 객체까지 영속성 컨텍스트에 저장할수있는 편리함을 제공한다.
CASCADE의 종류
ALL
PERSIST
MERGE,
REMOVE,
REFRESH,
DETACH
고아객체
부모 엔티티와 연관 관계가 끊어진 자식 엔티티를 고아객체라고 부른다. JPA는 고아객체를 자동으로 제거해주는 기능도 제공한다.매핑옵션에 irphanRemoval = true 옵션을 사용하면된다.
참고
'JPA' 카테고리의 다른 글
[JPA] QueryDSL 에서 FROM절에 SubQuery 및 ROW_NUMBER() 사용 방법 (1) | 2024.09.18 |
---|---|
[JPA] QueryDsl 를 이용한 무한 페이징(Infinite scrolling) - 성능 개선 (0) | 2024.05.02 |
[JPA] OSIV (1) | 2024.01.08 |
[JPA] 값 타입 (1) | 2024.01.05 |