반응형
안녕하세요.
코딩하는헬린이 입니다.
Spring data Jpa 만 사용해보다가 인프런에 김영한님의 JPA 자체 강의를 접하는 기회가 생겼습니다.
강의 내용의 토대로 정리해보도록 하겠습니다.
#프록시
Entity 실제 사용하는 시점에 데이터베이스에서 조회할 수 있다. 이와 관련 된 기술이 프록시 인데, 이 프록시를 통해서 즉시로딩 ( EAGER ) 과 지연로딩 ( LAZY ) 을 할 수 있다.
#메소드
- em.find() 는 DB를 통해서 실제 엔티티 객체를 바로 조회
- em.getReference() 는 DB의 조회를 미루는 가짜(프록시) 엔티티 객체를 조회
#예제
public void memberSelect() {
Member findMember = entityManager.find(Member.class, 1L);
System.out.println("findMember.id = " + findMember.getId());
System.out.println("findMember.username = " + findMember.getUsername());
}
find 메소드는 실제 엔티티를 바로 조회하기에 쿼리를 콘솔로 확인하면
아래와 같이 쿼리가 실행된뒤 output 내용이 보일겁니다.
select
member0_.member_id as member_i1_0_0_,
member0_.team_id as team_id3_0_0_,
member0_.username as username2_0_0_,
team1_.member_id as member_i1_1_1_,
team1_.name as name2_1_1_
from
member member0_
left outer join
team team1_
on member0_.team_id=team1_.member_id
where member0_.member_id=?
findMember.id = 1
findMember.username = hello
public void memberSelect() {
System.out.println("####");
Member findMember = entityManager.getReference(Member.class, 1L);
System.out.println("####");
}
####
####
위에 보시면 getReference 메소드를 사용시 호출을 하긴 하였는데 쿼리가 실행되지 않습니다.
그게 바로 조회를 미루기에 그렇습니다. 직접 값을 찾을때 db를 조회해서 가져옵니다.
public void memberSelect() {
System.out.println("####");
Member findMember = entityManager.getReference(Member.class, 1L);
System.out.println("####" + findMember.getUsername());
}
####
select
member0_.member_id as member_i1_0_0_,
member0_.team_id as team_id3_0_0_,
member0_.username as username2_0_0_,
team1_.member_id as member_i1_1_1_,
team1_.name as name2_1_1_
from
member member0_
left outer join
team team1_
on member0_.team_id=team1_.member_id
where member0_.member_id=?
findMember.username = hello
이렇게 entity 내부에 getUsername 1차 캐시를 가지고 있지 않기에 그 상황에 맞춰서 DB에 sql을 날립니다.
#프록시 메커니즘
Member m1 = entityManager.find(Member.class, member1.getId());
System.out.println("reference1 = " + m1.getClass());
Member m2 = entityManager.getReference(Member.class, member1.getId());
System.out.println("reference2 = " + m2.getClass());
System.out.println("a == a" + (m1 == m2)); //true
m1 = class com.jpa.jpa.Member
m2 = class com.jpa.jpa.Member
true
m1 자체가 이미 1차 캐싱하여 굳이 proxy로 가져올 필요가 없기에 jpa 성능 최적화?
로 인하여 Member 를 Class를 반환합니다.
* JPA는 한 트랜잭션에서 같은거를 보장해준다.
한 영속성 컨텍스트에서 가져온거면 true.
System.out.println(m1==m2) // true로 무조껀 만들어 줘야한다 : proxy가 아닌 실 값 가져옴
물론 반대로 프록시를 초기화해서 가져올경우 프록시로 반환합니다.
#프록시 특징
- 프록시 객체는 처음 사용할 때 한번만 초기화 된다.
- 프록시 객체를 초기화 할 때, 프록시 객체가 실제 엔티티로 바뀌는 것이 아니다. 초기화를 통해서 실제 엔티티에 접근이 가능해진 것이다.
- 프록시 객체는 원본 엔티티를 상속 받는다. 따라서 타입 체크시 == 비교가 아닌, instead of 로 타입을 비교해야 한다.
- 영속성 컨텍스트(1차 캐시)에 이미 찾는 엔티티가 존재하면, em.getReference()를 호출해도 실제 엔티티를 반환한다. (굳이 실객체가 있는제 프록시를 반환할 이유가 없다. 최적화 등의 이유로)
- 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화하면 문제 발생한다.
- em.close(), em.detach(entity) 를 하면 더이상 영속성 컨텍스트의 도움을 받을 수 없기 때문에 예외가 발생한다.
- 하이버네이트는 org.hibernate.LazyInitializationException을 터뜨린다.
반응형
'개발 > Java' 카테고리의 다른 글
[Java] Optional을 이용한 새로운 null 처리 (0) | 2020.12.19 |
---|