본문 바로가기

Spring boot

[spring boot] 3차 정리 (프록시, 즉시 로딩, 지연 로딩)

프록시

쓰는 이유 

member조회하는데 아주 가끔만 team까지 조회하고 보통은 name만 조회할때 team을 계속 조회하는건 낭비니까!

----------------------- 

1. getReference하면 프록시 생성 

2. getName()호출 : proxy target이 null이면 영속성 컨텍스트가 DB를 조회하고 실제 entity를 생성해 proxy와 연결해줌.

proxy target이 있으면 실제 entity인 target에 연결돼 속성 사용가능 

초기화 : DB를 통해 진짜 entity를 만들어내는 과정

---------------------------

proxy의 타입은 instanceof를 사용해야함.  (ex. m1 instanceof Member)

em.find() -> em.getReference()하면  둘다 진짜 entity

em.getReference()-> em.find()하면 둘다 proxy (둘이 같은 취급을 해야하기 때문에)

---------------------------

clear, detach, close()를 하면 준영속 상태로 영속성 컨텍스트를 이용할 수 없기 때문에 프록시 사용 불가 

---------------------------

getName()는  jpa가 프록시 강제 초기화 시키는 것

org.hibernate.Hibernate.initialize(entity);는 hibernate가 프록시 강제 초기화시키기 

   -> ex. Member refMember = em.getReference(Member.class, member1.getId());

            Hibernate.initialize(refMember);

-----------------

getId, getClass는 java상에서 이미 알고 있기 때문에 DB조회 안 해도 됨. 

다른 getName같이 java상에서 모르는걸 호출해야 DB조회(초기화)됨. 

 

 

 

 

즉시 로딩과 지연 로딩

실무! 가급적 지연로딩만 써라~ (즉시로딩은 테이블 여러개를 join해서 성능 낮출수도)

 

지연로딩 : 조인해야 되는건 proxy로 가져오고 실제로 데이터를 사용할 때 쿼리를 날려 진짜 객체 가져오는 것.

member를 조회할 때 가끔만 team까지 조회하고 보통 member만 조회할 때 

(fetch = fetchType.LAZY)하면 team은 proxy로 만들어짐. 

m.getTeam하면 team은 proxy를 가져옴. 

m.getTeam.getName()같이 실제 team에 터치하면 이 시점에 프록시 초기화가 된다.  DB조회 select쿼리가 날라감.

team에 대해 조회해야 할 땐 총 member와 team까지 쿼리가 두번 날라감

-------------------------------

즉시로딩 : 매번 member를 조회할 때마다 team까지 조회해야할때 

(fetch = fetchType.EAGER) 

member find할 때 부터 team과 join해서 한방에 가져옴.  (proxy가 없으니 초기화도 없음. 진짜 entity를 조회하는 것임)

----------------

즉시로딩 하면 안 되는 이유

JPQL에서 member조회를 하면 member의 team에 EAGER가 있어서 member 1개당 team까지 조회쿼리 날라감. 

N+1문제가 발생(처음 1개때문에 N개가 추가로 발생함)

member1은 teamA이고 member2는 teamB일때 

1. select m from Member m JPQL을 날리면 member1과 2가 조회되는select m from Member 쿼리 한개 

2. where member1의 team_id== team id인 team찾기 쿼리 한개 

3. where member2의 team_id== team id인 team찾기 쿼리 한개 

2,3은 영속성 컨텍스트에 존재 하지 않음으로 둘다 쿼리 날려야함. 

 

해결방안

실무!에서는 모든 연관관계를 지연로딩으로 깔고 한방에 A,B,C를 가져와야하면 fetch join사용 (즉시로딩은 사용 x)

1. fetch join(런타임에 동적으로 원하는애들만 join)

LAZY를 했음에도 join fetch m.team 를 하면 team에 데이터가 다 채워져서 반환됨 

핵심!  ManyToOne은 Lazy로 꼭 바꿔주기 

 

 


 

소개

application에서 필요한 최소한의 데이터를 DB에서 불러오려면 최대한 데이터를 필터링하고 group by까지 해서 가져와야함. 

jpql과 실행된 sql은 동적으로 코드짜기 어려움(문자열이라서) 그래서 자바코드로 jpql만드는 criteria 사용 (근데 sql스럽지도 않고 실무에서도 안씀)

그래서 쓰는게 queryDSL (동적쿼리 작성이 편하고 단순하고 쉬움. jpql과 1:1임, 실무 사용 권장함!!!!!)

 

em이용해서 createNativeQuery 할 때도 flush된다. = flush되는 때는 commit했을 때와 query날릴 때이다.

 

 

 

경로 표현식

컬렉션 상태 필드를 find하는건 가능 (상태필드여서)

!주의!  묵시적 조인(아래 내용 무시)말고 꼭 명시적 조인을 써라 

컬렉션 값 연관경로 : "select t.members.username From Team t" (묵시적 내부 조인이 발생하지만 username을 탐색할 수 없다. )

-> 이렇게 명시적으로 고치세요!!  : "select m From Team t join t.members m"; 

공부는 하지만 실무에서는 묵시적 조인 쓰지마~~~