시그마 삽질==six 시그마

JPA- 영속성 관리 본문

프로그래밍/JPA

JPA- 영속성 관리

Ethan Matthew Hunt 2020. 4. 10. 22:11
우아한 형제들의 김영한 팀장님의  '자바 ORM 표준 JPA 프로그래밍'을

구입하시길 강력 추천드립니다.

책 구입을 원하시는분은 요기를 클릭하시면 됩니다.

저자 직강 인프런 강의도 있습니다 궁금하신분은 요기를 클릭

하단의 내용은 제가 예전에 읽었던 내용을 요약 정리한 것입니다.

 

트랜잭션과(tx) 영속성 컨텍스트(pc) 생존 범위가 같다

tx 같으면 같은 pc /tx 다르면 다른 pc / 컨테이너는 스레드마다 다른 tx 할당 so 다른 pc기에 안전

 

준영속 상태와 지연로딩

tx 종료시 pc도 종료됨. 그럼 service나 repo계층은 pc유지되지만 controller나 view는 pc 가 준영속상태

준영속상태는 변경감지와 지연로딩 동작 안하기에 controller나 view에서 지연로딩 연관객체 호출시 에러발생 

 

 

방법1: view가 필요한 엔티티 미리 로딩

 

1)글로벌 패치 전략 수정

 

지연로딩->즉시로딩 변경(단점: 불필요 엔티티호출<-a화면은 필요, b화면은불필요, N+1문제발생)

 

JPA: 즉시로딩으로 설정시

 

 

 

@Entity
public class Board{

@Id
@Column(name=BOARD_ID)
private Long id;

@OneToMany(mappedBy="board")
private List<Reply> replies= new ArrayList<Reply>();

.....

}

em.find(Board.class);

 

실행된 SQL:

SELECT B.*,R.* FROM BOARD B OUTER  JOIN REPLY R  on B.BOARD_ID = R.BOARD_ID

 

 

문제는 JPQL임!!!

 

JPQL: 즉시로딩으로 설정시

JPA가 JPQL 분석해서 SQL을 생성할 때는 글로벌 패치전략을 참고하지 않고 오직 JPQL 자체만 사용한다. 처음엔 즉시로딩이든 지연로딩이든 구분앟고 JPQL 쿼리 자체에 충실하게 SQL을 만든다.

 

em.createQuery("select b from Board b",Board.class).getResultList();

 

select  b from Board b;

 

그런데 즉시로딩으로 설정되 있어서 JPA는 REPLY 컬렉션을 즉시 로딩하려고 다음 SQL을 추가로 실행한다.

 

SQL:

select * from BOARD;

select * from REPLY where BOARD_ID=1; //EAGER로 실행된  SQL BOARD 엔티티 수만큼 실행. board1과연관된 댓글

select * from REPLY where BOARD_ID=2;//board2와 연관된 댓글

select * from REPLY where BOARD_ID=3;//board3과연관된 댓글

.....

 

처음 실행한 SQL 결과 수만큼 추가로 SQL을 실행함. N+1문제 발생

 

2)JPQL 페치조인

 

페치조인시 SQL 조인을 사용해서 패치 조인 대상까지 한꺼번에 조회( N+1문제는해결, 단점: 불필요 엔티티호출)

 

JPQL:

 

select b from Board b join fetch b.Reply 

 

실행된 SQL:

 

SELECT B.*,R.* FROM BOARD B OUTER  JOIN REPLY R  on B.BOARD_ID = R.BOARD_ID

 

FETCH.EAGER 1:1 조회된 대상수만큼 개별쿼리로 연관객체  조회함. n+1문제 발생 ->fetch join으로 해결
다:1 조회된 대상수만큼 개별쿼리로 연관객체  조회함. n+1문제 발생 ->fetch join으로 해결
1:다 조회된 대상수만큼 개별쿼리로 연관객체  조회함. n+1문제 발생 ->fetch join으로 해결
fetch join으로인해 추가로 중복결과문제->distinct사용
FETCH.LAZY 1:1 JPQL은 N+1 문제 x. But biz logic에서 (조회된 결과에대해 for문 돌려서)  결과에 연결된 연관객체 사용한만큼  n+1문제발생 가능 -> fetch join으로 해결
다:1 JPQL은 N+1 문제 x. But biz logic에서 (조회된 결과에대해 for문 돌려서)  결과에 연결된 연관객체 사용한만큼  n+1문제발생 가능 -> fetch join으로 해결
1:다 JPQL은 N+1 문제 x. But biz logic에서 (조회된 결과에대해 for문 돌려서)  결과에 연결된 연관객체 사용한만큼  n+1문제발생 가능 -> fetch join으로 해결
fetch join으로인해 추가로 중복결과문제->distinct사용

 

하이버네이트 @BatchSize 로 n+1문제 해결..

 

 

 

 

 

3) 강제로 초기화

 

initalize() 메소드 사용해서 프록시 갖에로 초기화

 

4) FACADE 계층 추가

 

프레젠테이션 계층과 서비스 계층사이에 FACADE 계층을둠

 

 

방법2 :OSIV(Open Session In View)사용 

1) 요청당 OSIV 방식

 

영속성 컨텍스트를 view까지 열어둔다.(엔티티를 항상 영속 상태로 유지)

방법은 필터나 인터셉터에서 트랜잭션을 시작해서 요청 긑날때 트랜잭션도 끝내는, 즉 전기간에 사용

문제점은 프레젠테이션 계층에서 엔티티 변경할 수 있음

 

 

 

 

2) 스프링 제공 OSIV

 

-flow-

spring.jpa.open-in-view=true

 

client 요청시 필터나 인터셉터에서 pc생성. tx 미생성.

서비스계층 tx생성

서비스 tx 종료시 pc도 원래 종료인데 pc는 뷰단에 살려둔다.

그래서 요기서 lazy loading하고 할 수 있음.

 

만약 뷰단에서 엔티티수정해도 em.flush 안일어나고 em.close 일어남

em.flush 강제로 해도 트랜잭션 범위 밖이라 에러남.

 

1)그런데 수정한걸  서비스단으로 넘기면 문제

2) 커넥션을 계속 물고있고 서블릿 필터나, 스프링 인터셉터로 요청이 돌아가야 커넥션을 돌려줘서 비용큼

 

        뷰  서비스   다시 뷰

pc   o        o         o

tx    x        o          x

 

 

해결방법

1. spring.jpa.open-in-view=false 

2. 엔티티를 읽기전용 인터페이스로 제공 (setter x)

3. 엔티티 매핑(읽기전용 래퍼객체 반환 )

4. DTO만 반환

5. 뷰단에는 set하지말자고 합의

 

'프로그래밍 > JPA' 카테고리의 다른 글

JPA 성능 최적화  (0) 2020.08.08
트랜잭션과 락 ,2차 캐시  (0) 2020.04.11
스프링 데이터 JPA  (0) 2020.04.10
JPA -객체 지향 쿼리언어  (0) 2020.04.09
JPA -값 타입  (0) 2020.04.09
Comments