실제 클래스를 상속 받아서 만들어집니다.
실제 클래스와 겉 모양이 같습니다.
사용하는 입장에서는 진짜 객체인지 프록시 객체인지 구분하지 않고 사용하면 됩니다.
프록시 객체는 실제 객체의 참조(target)를 보관 합니다.
프록시 객체를 호출하면 프록시 객체는 실제 객체의 메소드를 호출 합니다.
Member member = em.getReference(Member.class, "id1");
member.getName();
프록시 객체는 처음 사용할 때 한 번만 초기화
프록시 객체를 초기화 할 때, 프록시 객체가 실제 엔티티로 바뀌는 것은 아닙니다. 초기화되면 프록시 객체를 통해서 실제 엔티티에 접근 가능하게 됩니다.
프록시 객체는 원본 엔티티를 상속 받습니다. 따라서 타입 체크시 주의해야합니다.
(== 비교 실패, 대신 instance of 사용)
영속성 컨텍스트에 찾는 엔티티가 이미 있으면 em.getReference()를 호출해도 실제 엔티티 반환합니다.
영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화하면 문제가 발생합니다.
(org.hibernate.LazyInitializationException 예외를 터트림)
프록시 인스턴스의 초기화 여부 확인
PersistenceUnitUtil.isLoaded(Object entity)
프록시 클래스 확인 방법
entity.getClass().getName() 출력
프록시 강제 초기화
org.hibernate.Hibernate.initialize(entity)
JPA 표준은 강제 초기화 없음
호출을 통해서 강제 초기화해줌 member.getName()
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String name;
//********LAZY 지연로딩*******ㄴ
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "TEAM_ID")
private Team team;
...
}
Member member = em.find(Member.class, 1L);
Team team = member.getTeam();
team.getName(); //실제 team을 사용하는 시점에 초기화(DB조회)
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String name;
//********EAGER 즉시로딩*******ㄴ
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "TEAM_ID")
private Team team;
...
}
프록시와 즉시로딩 주의
- 가급적 지연 로딩만 사용(특히 실무에서)
- 즉시 로딩을 적용하면 예상하지 못한 SQL이 발생
- 즉시 로딩은 JPQL에서 N+1 문제를 일으킵니다.
- @XXXToOne은 기본이 즉시 로딩이므로
LAZY로 설정해줘야 합니다.- @XXXToMany는 기본이 지연 로딩입니다.
모든 연관관계에 지연 로딩을 사용해라!
실무에서 즉시 로딩을 사용하지 마라!
JPQL fetch 조인이나, 엔티티 그래프 기능을 사용해라!
즉시 로딩은 상상하지 못한 쿼리가 나갑니다.
@OneToMany(mappedBy="parent", cascade=CascadeType.PERSIST)
CASCADE 주의!
- 영속성 전이는 연관관계를 매핑하는 것과 아무 관련이 없습니다.
- 엔티티를 영속화할 때 연관된 엔티티도 함께 영속화하는 편리함을 제공할 뿐 입니다.
ALL : 모두 적용
PERSIST : 영속
REMOVE : 삭제
MERGE : 병합
REFRESH : REFRESH
DETACH : DETACH
고아 객체 제거 : 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제 합니다.
orphanRemoval = true
주의
- 참조하는 곳이 하나일 때 사용해야함!
- 특정 엔티티가 개인 소유할 때 사용해야함!
- @OneToOne, @OneToMany 만 가능
CascadeType.ALL + orphanRemoval=true
스스로 생명주기를 관리하는 엔티티는 em.persist()로 영속화, em.remove()로 제거 합니다.
두 옵션을 모두 활성화 하면 부모 엔티티를 통해서 자식의 생명 주기를 관리할 수 있습니다.
[Reference]