JPA주의 사항 및 성능 최적화
JPA주의 사항 및 성능 최적화
- bulk 연산을 할때는 영속성 컨텍스트의 값을 무시하고 바로 DB에 질의문을 던진다. 때문에 bulk연산 이후 이전에 영속성 컨텍스트에서 관리하는 데이터는 변경되지 않은 상태이다.
- 변경된 내용을 조회하고자 할경우 EntityManager에서 bulk연산 전에 flush/clear를 수행해줘서 영속성 컨텍스트 내용이 DB에 반영될 수 있도록 하고 영속성 컨텍스트를 비워준 후 다시 조회한다.
- in spring jpa : @Modifying(clearAutomatically = true)
- JDBC Template, myBatis 등 직접 쿼리하는 방식과 혼용할 경우 flush, clear를 적절하게 사용해서 영속성 컨텍스트 내 무결성이 깨지지 않도록 한다.
-
연관관계 설정시 mappedBy는 FK가 없는 Entity쪽에 작성한다.
-
Entity를 response, request객체로 사용하지 말아라
-
데이터 전송 스펙과 entity는 다를 수 있어 api명세가 실제 화면단과의 커뮤니케이션에 괴리감이 생길 수 있다.
-
entity가 오염된다.
-
entity에 프리젠테이션 계층을 위한 로직이 추가된다.
-
엔티티의 모든값이 노출된다
-
응답스펙을 맞추기위한 로직이 추가된다.(양방향 참조시 @JsonIgnore등)
-
엔티티가 변경되면 API스펙이 변한다.(커뮤니케이션 문제 발생)
-
추가로 컬렉션을 직접 반환하면 향후 API스펙을 변경하기 어렵다.
-
array를 반환하는 json에 추가 attribute를 추가 해야 하는 경우 등.(아래와 같이 반환하자)
{ "count":4, "data":[ datas... ] }
-
-
-
별도로 데이터 전송용DTO를 만들어 활용하자
-
- update에는 변경감지를 이용하자
- 데이터를 불러와서 영속성 컨텍스트에 올려놓은 후 데이터를 변경하여 자동으로 update가 되도록 하자
- XToOne관계의 fetch전략은 LAZY로 설정하자
- EAGER가 필요한곳만 따로 설정하도록 한다.
- 필요시 JPQL의 fetch join을 활용하자
- 일반 join과의 차이점
- 일반 join : from절에 걸린 대상만 영속성 컨텍스트에 올려주고 나머지는 proxy만을 담는다. 때문에 LAZY로 설정된 entity의 컬럼은 select절에서 다루지 않는다.
- fetch join : 쿼리상에 있는 모든정보를 실제 조회한다. 실제 select문에서도 컬럼명 지정시 join에 걸린 컬럼들도 함께 지정되는것을 확인할 수 있다.
- 조회하는 컬럼수가 많을 경우
select new [DTO클래스] from
처럼 new 키워드로 가져오고 싶은 컬럼만 따로 지정해서 가져온다.
- 일반 join과의 차이점
- 1:관계에서 join에 의해 행의 개수가 많아질 경우
- jpql 의 distinct 키워드 사용
- 실제 쿼리가 distinct로 나가긴 하지만 조회된 결과가 중복되면 jpa에서 한번더 distinct를 수행해 준다.
- 이때는 페이징 쿼리가 적용되지 않는다.
- 페이징 쿼리는 메모리에서 처리된다.(데이터가 많을 경우 치명적)
- 배치 사이즈 조절
- XToOne관계 까지만 fetch join으로 조회한다.(쿼리로 페이징 처리 가능)
- 이 후 LAZY로딩 되는 객체를 조회하면 where-in절이 적용된다.
- spring.jpa.properties.hibernate.default_batch_fetch_size값을 조절한다.
- 이 경우 LAZY로딩으로 조회되는 XToMany관계의 entity는 where ~ in 쿼리로 조회된다.(설정 된 값을 기준으로 in 쿼리 내의 조건 개수가 설정된다. DBMS한계로 인해 100~ 1000개 권장.)
- 항목별로 적용하고 싶을 경우 해당 변수에 @BatchSize(n) 을 설정한다.
- 별도로 N관계에 있는 entity만 조회해도된다.
- for문 돌면서.. distinct, where-in 이럴 필요 없이.. 전통적으로!
- 그래도 N관계의 쿼리가 1에 있는 개수만큼 돌기 때문에 완전히 해결된것은 아니다.
- N관계에 있는 쿼리는 1에서 조회된 key값 만큼 where-in절에 걸어서 돌려준다.
- 그럼 2번만 돈다.(1번째 : 본쿼리 ,2번째 : N관계의 where-in쿼리)
- Map을 사용해서 N관계의 쿼리를 본쿼리 결과에 매핑시켜준다.
- jpql 의 distinct 키워드 사용
- OSIV(open session in view)
- spring.jpa.open-in-view=true 로 설정되며 이는 기본값이다.
- false일 경우 Transaction안에서만 영속성 컨텍스트가 유지된다.
- Service단에서 데이터를 조회하고 Transaction이 끝나더라도 영속성 컨텍스트는 계속 유지가 된다.
- 이때문에 DB 커넥션이 반환이 되지 않는다.
- 이유는 controller단에서 lazy객체를 호출할 수 도 있기 때문인데, view가 렌더링되고 response가 다 보내지면 커넥션이 반환되고 영속성 커넥션도 사라지게 된다.
- 실시간 처리가 중요한 서비스에서는 장애로 이어질 수 있다.
- OSIV를 끌 경우
- LAZY로딩을 Transaction안에서 모두 끝내야 한다.(@Service단)
- spring.jpa.open-in-view=true 로 설정되며 이는 기본값이다.
-
힌트
- @QueryHints를 이용해 JPA에서 제공하는 힌트 사용
- org.hibernate.readOnly등..
- @QueryHints를 이용해 JPA에서 제공하는 힌트 사용