김영한님의 자바 ORM 표준 JPA 프로그래밍 - 기본편 강의를 듣고 정리한 내용입니다.
https://www.inflearn.com/course/ORM-JPA-Basic
실제 클래스를 상속받아 만들어져서, 실제 클래스와 겉모양이 같다.
사용하는 입장에서는 진짜 객체인지 프록시 객체인지 구분하지않고 사용하면 된다.
프록시 객체는 실제 객체의 참조(target)을 보관하고있다.
프록시 객체를 호출하면 프록시 객체는 실제 객체의 메서드를 호출한다.
em.getReference(): 데이터베이스 조회를 미루는 가짜(프록시) 엔티티 객체 조회
초기에는 target에 null이고, 영속성 컨텍스트에 초기화를 요청한다.
이후 영속성 컨텍스트는 DB를 조회해서 실제 엔티티 객체를 생성하여 target에 연결시켜준다.
target으로 실제 객체와 연결되면, 실제 객체의 메서드를 호출한다.
JPA는 하나의 트랜잭션 내에서 ==이 알맞게 동작하도록 상황에 따라 다르게 작동한다.
ex) 아래와 같은 코드에서 2번째 find한 findMember는 proxy객체가 반환된다.
→ 이처럼 JPA는 한 트랜잭션 내에서 실제 엔티티 객체와 프록시 객체의 비교연산 동작의 완전성을 보장하기 위해 두 객체의 클래스 타입이 동일하다. refMember 와 findMember의 == 은 true 이다.
but, 동일한 트랜잭션이 아닌경우, ==을 사용한다면 상황에 따라 결과가 달라질 수 있기 때문에 instance of 를 사용하는것이 좋다.
아래 코드와 같이 em.detach()나 em.close(), em.clear()로 영속성 컨텍스트에서 detach시키거나 닫아서
준영속 상태에서 getUsername을 호출하면 LazyInitializationException 예외가 발생한다.
참고로 JPA 표준은 강제 초기화가 없어서 강제로 호출해야한다.
강제 호출: member.getName()
비즈니스로직상에서 단순히 회원 정보만 필요하고 팀 정보는 필요없을때, 회원을 조회할때 팀을 함께 조회하는것은 성능상 손해이다. 따라서 이를 지연로딩으로 설정하면, 연관관계에 관한 값을 요청할 때 DB에 query를 보내는 방식으로 동작한다.
연관관계 매핑에서 fetch = FetchType.LAZY 으로 설정된 엔티티는 프록시로 가져온다.
실제로 그 엔티티를 사용하는 시점에 초기화가 된다.(DB조회)
fetch = FetchType.EAGER 로 즉시 로딩하여 가져오기때문에 프록시를 쓸 일이 없다. 이미 초기화가 다 끝나있는 상태이다.
특정 엔티티를 영속 상태로 만들때 연관된 엔티티도 함께 영속상태로 만들고 싶을때 사용한다.
ex) 부모 엔티티를 저장할때 자식 엔티티도 함께 저장
연관관계 매핑의 cascade 속성 - cascade=CascadeType.Cascade타입
CASCADE 종류
부모를 persist 할때, cascade 지정된 매핑된 엔티티도 함께 persist 해준다
엔티티를 영속화할 때 연관된 엔티티도 함께 영속화하는 편리함을 제공할 뿐 영속성 전이는 연관관계를 매핑하는 것과 아무 관련이 없음
하나의 엔티티가 해당 엔티티를 관리할때(종속적일때) 의미가 있다. 라이프 사이클이 동일하고, 단일 소유자일때 사용하면 된다. 다른 엔티티에서도 관리하고 있을때는 사용하면 안된다!!
부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제한다.
참조가 제거된 엔티티는 다른곳에서 참조하지 않는 고아 객체로 보고 삭제하는 기능이다.
연관관계 매핑의 고아객체 속성 - orphanRemoval = true
❗️ 주의
참고 : 개념적으로 부모를 제거하면 자식은 고아가 된다. 따라서 고아 객체 제거 기능을 활성화 하면, 부모를 제거할 때 자식도 함께 제거된다. 이것은 CascadeType.REMOVE처럼 동작한다.
스스로 생명주기를 관리하는 엔티티는 em.persist()로 영속화, em.remove()로 생명주기를 제거한다.
두 옵션을 모두 활성화 하면 부모 엔티티를 통해서 자식의 생명주기를 관리할 수 있다.
CascadeType.ALL + orphanRemovel=true (영속성 전이 + 고아객체)