10. 객체 지향 쿼리 언어 2- 중급 문법
1. 경로 표현식
- .을 찍어서 객체 그래프를 탐색
- 상태 필드 >> 단순히 값을 저장하기 위한 필드(m.username)
- 경로 탐색의 끝, 탐색 x
String query = "select m.username from Member m";
- 연관 필드 >> 연관관계를 위한 필드
- 단일 값 연관 필드 >> @ManyToOne, @OneToOne, 대상이 엔티티
- 묵시적 내부 조인 발생, 탐색 O -> 쿼리 튜닝이 어렵다
String query = "select m.team.name from Member m";
>> team에서 타고 타고 값을 찾을 수 있다.
- 컬렉션 값 연관 필드 >> @ManyToOne, @OneToOne, 대상이 엔티티
- 묵시적 내부 조인 발생, 탐색 x
String query = "select t.members from Team t";
- 묵시적 조인을 사용하지 말고 명시적 조인을 사용할 것
2. 페치 조인
- 연관된 엔티티나 컬렉션을 sql 한 번에 함께 조회하는 기능
- 지연로딩보다 페치 조인이 우선
- select m from Member m join fetch m.team
- select m., t. from member m inner join team t on m.team_id = t.id
- 차이점
String query = "select m from Member m";
List<Member> result = em.createQuery(query, Member.class)
.getResultList();
for (Member member : result) {
System.out.println("member = " + member.getUsername()+", "+ member.getTeam().getName());
//회원1, 팀A(SQL)
//회원2, 팀A(1차 캐시)
//회원3, 팀B(SQL)
}
>> 위 코드는 getTeam().getName()을 호출할 때 Team 조인 쿼리를 실행
>> 회원 100명을 조회하면 조인 쿼리가 100번까지 나갈 수 있음 -> N+1 문제
String query = "select m from Member m join fetch m.team";
>> 한번의 조인 쿼리로 연관된 데이터를 가져온다.
- 컬렉션 패치 조인
- 일대다 쿼리로 인해 데이터가 늘어난다.
- JPQL의 District -> 같은 식별자를 가진 데이터를 제거해준다.
- 일대다 쿼리로 인해 데이터가 늘어난다.
- 페치 조인과 일반 조인의 차이
- 일반 조인 실행 시 연관된 엔티티를 함께 조회하지는 않음
- 페치 조인은 사용할 때만 연관된 엔티티도 함께 조회(즉시 로딩)
- 페치 조인의 한계
- 페치 조인 대상에는 별칭을 줄 수 없다(하이버네이트는 가능하지만 가급적 사용 x)
- 단 페치 조인 대상에 이중 조인을 걸 떄는 사용 - 둘 이상의 컬렉션은 페치 조인 불가
- 컬렉션을 페치 조인하면 페이징 api 사용 불가
- 하이버 네이트는 경고 로그를 남기고 메모리에서 페이징(매우 위험)
=> 해결 방안: 일대 다 관계를 다대일 관계로 뒤집어서 해결
=> 해결 방안: 페치 조인을 과감하게 뺴고 페이징 실행 -> @BatchSize 사용 여러개
=> 해결 방안: DTO를 생성해서 새로운 객체를 뽑아 온다.
- 페치 조인 대상에는 별칭을 줄 수 없다(하이버네이트는 가능하지만 가급적 사용 x)
@BatchSize(size = 100)
@OneToMany(mappedBy = "team", fetch = FetchType.LAZY)
private List<Member> members = new ArrayList<>();
---------------------------------------------------------------
<property name="hibernate.default_batch_fetch_size" value="100" />
===> 여러 테이블을 조인해서 엔티티가 가진 모양이 아닌 전혀 다른 결과를 내야하면 페치 조인 보다는 일반 조인을 사용하고 필요한 데이터들만 조회해서 DTO로 반환하는 것이 효과적인다.
다형성 쿼리
- 조회 대상을 특정 자식으로 한정
- SELECT I FROM ITEM I WHERE TYPE(I) IN (BOOK, MOVIE)
- TREAT
- 자바의 타입 캐스팅과 유사
- 상속 구조에서 부모 타입을 특정 자식 타입으로 변환
- SELECT I FROM ITEM I WHERE TREAT(I AS BOOK).author = 'kim'
4. 엔티티 직접 사용
- 기본 키 값
- String query = "select m from Member m where m =:member";
-> 알아서 식별자를 통해 WHERE 문으로 비교한다.
-> M.TEAM :TEAM --> 외래키도 알아서 비교 가능하다.
- String query = "select m from Member m where m =:member";
5. Named 쿼리
- 어노테이션으로 미리 쿼리를 선언하고 이름 부여
- 정적 쿼리만 가능
- 어노테이션 xml 정의
- 애플리케이션 로딩 시점에 초기화 후 재사용
- 애플리케이션 로딩 시점에 검증
@Entity
@NamedQuery(
name ="Member.findByUsername",
query = "select m from Member m where m.username = :username"
)
-----------------------------------------------------------------------
List<Member> memberList = em.createNamedQuery("Member.findByUsername", Member.class)
.setParameter("username", member1.getUsername())
.getResultList();
- 쿼리가 문자열인데도 불구하고 컴파일에러가 발생한다,
6. 벌크 연산
- 쿼리 한번으로 여러 테이블의 로우 변경
int count = em.createQuery("update Member m set m.age = 20")
.executeUpdate();
- 벌크 연산은 영속성 컨텍스트를 무시하고 데이터베이스에 직접 쿼리 -> 업데이트를 해도 1차 캐시는 수정이 되어있지 않는다.
-> 벌크 연산을 먼저 수행 후 영속성 컨텍스트 초기화 -> em.clear() 후 db 다시 조회
- createQuery를 실행하므로 자동 플러시 수행
Author And Source
이 문제에 관하여(10. 객체 지향 쿼리 언어 2- 중급 문법), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@homelala/10.-객체-지향-쿼리-언어-2-중급-문법저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)