본문 바로가기

웹 프로그래밍/JPA

객체 지향 쿼리 언어_중급

경로 표현식

-점을 찍어 객체 그래프를 탐색하는 것

select m.username (상태 필드)

    from Member m

        join m.team t (단일 값 연관 필드)

        join m.orders o (컬렉션 값 연관 필드)

where t.name = '팀A'

 

-상태 필드(state field): 단순히 값을 저장하기 위한 필드

-연관 필드(association field): 연관관계를 위한 필드

    -단일 값 연관 필드: @ManyToOne, @OneToOne, 대상이 엔티티

    -컬렉션 값 연관 필드: @OneToMany, @ManyToMany, 대상이 컬렉션

 

경로 표현식 특징

-상태 필드: 경로 탐색의 끝, 탐색 못함

-단일 값 연관 경로: 묵시적 내부 조인(inner join) 발생, 탐색 O

    -묵시적은 가급적 안쓰는 방향(JPQL와 SQL를 유사하게 짜야함)

-컬렉션 값 연관 경로: 묵시적 내부 조인 발생, 탐색 못함

    -FROM절에서 명시적 조인을 통해 별칭을 얻으면 별칭을 통해 탐색 가능

    -e.g.) select m.username From Team t join t.members m

 

명시적 조인, 묵시적 조인

-명시적 조인: join 키워드 직접 사용

    -e.g.) select m from Member m join m.team t

-묵시적 조인: 경로 표현식에 의해 묵시적으로 SQL 조인 발생

    -e.g.) select m.team from Member m

 

JPQL 페치 조인(fetch join) : 실무 필수

-SQL 조인 종류 X

-JPQL에서 성능 최적화를 위해 제공하는 기능

-연관된 엔티티나 컬렉션을 SQL 한번에 함께 조회하는 기능

-join fetch 명령어

-페치 조인 ::= [ LEFT [OUTER] |  INNER  ] JOIN FETCH 조인경로

 

엔티티 페치 조인

-회원을 조회하면서 연관된 팀도 함께 조회

-(JPQL) select m from Member m join fetch m.team

-(SQL) SELECT M.* T.* FROM MEMBER M INNER JOIN TEAM T ON M.TEAM_ID = T.ID

-지연로딩으로 쿼리가 N+1개 발생 -> 무조건 fetch join으로 해결

 

컬렉션 페치 조인

-일대다 관계

-(JPQL) select t from Team t join fetch t.members where t.name = '팁A'

-(SQL) SELECT T.* M.* FROM TEAM T INNER JOIN MEMBER M ON T.ID = M.TEAM_ID WHERE T.NAME = '팀A'

-DB 입장에서 컬렉션 조인하면 데이터 뻥튀기 됨 (e.g. 팀 입장에서는 하난데 컬렉션 크기에 따라 테이블 행이 추가)

 

페치 조인과 DISTINCT

-SQL의 DISTINCT는 중복된 결과를 제거하는 명령(SQL에서는 완전히 똑같아야 제거)

-JPQL의 DISTINCT 2가지 기능 제공

    -1.SQL에 DISTINCT를 추가

    -2.애플리케이션에서 엔티티 중복 제거(같은 식별자를 가진 엔티티 제거)

 

페치 조인과 일반 조인의 차이

-일반 조인은 실행시 연관된 엔티티를 함께 조회하지 않음

-(JPQL) select t from Team t join t.members m where t.name = '팀A' ( Team만 퍼올리고 Member는 초기화가 안돼서 쿼리 따로 나가야함 페치 조인은 쿼리 한방에 끝남)

-JPQL은 결과를 반환할때 연관관계 고려X

-단지 SELECT절에 지정한 엔티티만 고려 할뿐

-페치 조인을 사용할때만 연관된 엔티티도 함께 조회(즉시 로딩)

-페치 조인은 객체 그래프를 SQL 한번에 조회하는 개념(실무) -> N+1 문제 해결

 

페치 조인의 특징과 한계

-페치 조인 대상에는 별칭을 줄수 없음

-둘 이상의 컬렉션은 페치 조인 할수 없음

-컬렉션을 페치 조인하면 페이징 API를 사용할수 없음

    -일대일, 다대일 같은 단일 값 연관필드들은 페치 조인해도 페이징 가능(데이터 뻥튀기가 안되니까)

    -일대다는 @BatchSize(size = 100)을 이용해서 배치만큼 퍼올려 페치 조인 하여 해결 가능

-엔티티에 직접 적용하는 글로벌 로딩 전략(LAZY)보다 우선순위가 높음

-최적화가 필요한 곳(N+1)은 페치 조인 적용

-페치 조인은 객체 그래프를 유지할때 사용하면 효과적

-여러 테이블은 조인해서 엔티티가 가진 모양이 전혀 아닌 다른 결과를 내야하면, 페치 조인보다는 일반 조인을 사용하고 필요한 데이터들만 조회해서 DTO로 반환하는것이 효과적

 

다형성 쿼리

-TYPE

    -조회 대상을 특정 자식으로 한정

    -(JPQL) select i from Item i where type(i) IN (Book, Movie)

    -(SQL) select i from i where i.DTYPE in ('B', 'M')

 

-TREAT

    -자바의 타입 캐스팅과 유사

    -상속 구조에서 부모 타입을 특정 자식 타입으로 사용하고 싶을때

    -FROM, WHERE, SELECT 사용

    -(JPQL) select i from Item i where treat(i as Book).auther = 'kim'

    -(SQL) select i.* from Item i where i.DTYPE = 'B' and i.auther = 'kim'

 

엔티티 직접 사용-기본 키 값

-JPQL에서 엔티티를 직접 사용하면 SQL에서 해당 엔티티의 기본 키 값을 사용

-파라미터(setParameter())로 전달해도 됨

-(JPQL) select count(m.id) from Member m

-(JPQL) select count(m) from Member m

-(두 JPQL 모두 다음 SQL) select count(m.id) as cnt from Member m

 

Named 쿼리

-미리 정의해서 이름을 부여해두고 사용하는 JPQL

-정적 쿼리

-어노테이션, XML에 정의

    -@NameQuery(

                   name = "Member.findByUsername",

                   query = "select m from Member m where m.username = :username")

-애플리케이션 로딩 시점에 초기화 후 재사용

-애플리케이션 로딩 시점에 쿼리를 검증(컴파일 시점에 오류 발견가능)

-스프링 부트에서 편리하게 QUERY로 바로 사용

 

벌크 연산

-한건을 뽑아보는 것을 제외한 모든 update, delete문

-변경된 데이터가 100건이라면 100번의 UPDATE SQL 발생하는 문제

-쿼리 한번으로 여러 테이블 Row 변경

-executeUpdata()의 결과는 영향받은 엔티티 수 반환

-UPDATE, DELETE 지원

-INSERT(insert into .. select, 하이버네이트 지원)

 

벌크 연산 주의

-벌크 연산은 영속성 컨텍스트를 무시하고 데이터베이스에 직접 쿼리

-해결책

    -1.벌크 연산을 먼저 실행(맨 처음 실행시)

    -2.벌크 연산 수행후 영속성 컨텍스트 초기화(DB는 변했어도 영속성 컨텍스트는 이전값이므로 clear()로 초기화)

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

JPA 활용(2) 팁 정리  (0) 2023.03.16
JPA 활용(1) 팁 정리  (0) 2023.03.08
객체 지향 쿼리 언어_기본  (0) 2023.03.07
기본값 타입, 임베디드 타입  (0) 2023.03.06
영속성 전이(CASCADE)와 고아 객체  (0) 2023.03.04