[JPA] JPQL - Join, 서브 쿼리, 경로 표현식

3Beom's 개발 블로그·2023년 6월 18일
0

SpringJPA

목록 보기
15/21
post-custom-banner

출처

본 글은 인프런의 김영한님 강의 자바 ORM 표준 JPA 프로그래밍 - 기본편 을 수강하며 기록한 필기 내용을 정리한 글입니다.

-> 인프런
-> 자바 ORM 표준 JPA 프로그래밍 - 기본편 강의


조인

  • 내부 조인 (inner join)
    • SELECT m FROM Member m [INNER] JOIN [m.team](http://m.team) t

    • 엔티티를 대상으로 작성되기 때문에 참조형(m.team)으로 표현된다.

      String query = "select m from Member m inner join m.team t";
      List<Member> result = em.createQuery(query, Member.class)
              .getResultList();
  • 외부 조인 (outer join)
    • SELECT m FROM Member m LEFT [OUTER] JOIN [m.team](http://m.team) t

      String query = "select m from Member m left join m.team t";
      List<Member> result = em.createQuery(query, Member.class)
              .getResultList();
  • 세타 조인
    • SELECT count(m) FROM Member m, Team t where m.username = t.name

    • 아무 관련 없는 두 엔티티 사이에 특정 조건을 걸어서 조인을 수행하는 것.

      String query = "select m from Member m, Team t where m.username = t.name";
      List<Member> result = em.createQuery(query, Member.class)
              .getResultList();
    • cross join 후 where 절에 해당하는 데이터들만 가져옴

ON 절

  • join 대상을 join 하기 전에 필터링해서 줄인 다음 가져올 수 있음
  • JPQL
    • SELECT m, t FROM Member m LEFT JOIN [m.team](http://m.team) t ON [t.name](http://t.name) = 'A'
  • SQL
    • SELECT m.*, t.* FROM Member m LEFT JOIN Team t ON m.team_id = t.team_id and t.name='A'
  • 연관관계가 없는 엔티티 외부 조인도 가능
    • ex) 회원의 이름과 팀 이름이 같은 대상 외부 조인
    • JPQL
      • SELECT m, t FROM Member m LEFT JOIN Team t ON m.username=t.name
      • m.team t 가 아닌 Team t : 아무 관계 없는 엔티티 사이의 조인
    • SQL
      • SELECT m.*, t.* FROM Member m LEFT JOIN Team t ON m.username=t.name

서브쿼리

  • SQL과 같이 서브쿼리도 지원된다.
  • 나이가 평균보다 많은 회원
    • select m from Member m where m.age > (select avg(m2.age) from Member m2)
  • 한 건이라도 주문한 고객
    • select m from Member m where (select count(o) from Order o where m = o.member)
  • EXISTS, IN, ALL, ANY 등 모두 활용 가능
  • 하지만 JPA의 서브쿼리에는 중요한 한계점이 존재한다. ⇒ JPA의 표준 스펙에서는 WHERE, HAVING 절에서만 서브 쿼리를 사용할 수 있다. 하지만 Hibernate에서는 SELECT 절에서도 서브 쿼리를 사용할 수 있다.

FROM 절의 서브 쿼리는 현재 JPQL에서 불가능하다. → JOIN으로 풀 수 있으면 풀어서 해결해야 한다.

경로 표현식

  • 상태 필드 : 그냥 값을 저장하기 위한 필드
  • 연관 필드 : 연관 관계를 위한 필드
    • 단일 값 연관 필드 : @ManyToOne, @OneToOne . 엔티티 내에서 컬렉션이 아닌 엔티티 자체를 필드로 두고 있는 것. (N : 1, 1 : 1 관계에서 연관관계 주인이 갖고 있는 대상 엔티티 필드)
    • 컬렉션 값 연관 필드 : @ManyToMany, @OneToMany . 엔티티 내에서 연관관계 대상 엔티티 필드를 컬렉션으로 갖고 있는 것.
  • 각 경로 표현식의 특징
    • 상태 필드 : 경로 탐색의 끝. 탐색 안됨
      • select m.username from Member m 이렇게 그냥 상태 필드 자체로 끝남.
      • m.username.~~ 이렇게 더 타고 못들어감
    • 단일 값 연관 경로 : 묵시적 내부 조인(inner join)이 발생함. 탐색 가능
      • select [m.team.name](http://m.team.name) From Member m 이렇게 필드 자체로 끝나지 않고, 더 이어서 타고 들어갈 수 있다.
      • 묵시적 내부 조인이 발생한다.
        • Member와 Team 테이블 join 되고, Team 테이블의 컬럼들이 Select 절에 들어가게 되는 것.
        • 하지만 이렇게 묵시적으로 내부 조인이 발생하는 기능은 웬만하면 쓰지 않는게 좋다. 나중에 튜닝하기에 까다롭다.
    • 컬렉션 값 연관 경로 : 묵시적 내부 조인(inner join) 발생, but 탐색 불가능
      • select t.members from Team t 이렇게 member 리스트 필드를 가져옴.
      • 하지만 t.members.~~ 이렇게 더 타고 들어갈 순 없다. (Collection 타입으로 반환되도록 설정되어 있어 size 정도 말고는 더 타고 들어갈 수 없다.)
      • 이것도 묵시적 내부 조인이 발생한다.
      • 하지만 만약 명시적으로 join할 경우, 별칭을 통해 탐색이 가능해진다.
        select m.username from Team t join t.members m
        → Team과 해당 Team의 members를 join한 후, 각 member에 접근하는 것.

경로 탐색을 사용한 묵시적 조인 시 주의사항

  • 묵시적 조인은 항상 내부 조인(inner join)만 이루어진다.
  • 컬렉션은 추가적인 탐색이 안된다. 추가 탐색 하려면 명시적 조인을 통해 별칭을 얻어야 한다.
  • 경로 탐색은 주로 SELECT, WHERE 절에서 사용되지만, 묵시적 조인으로 인해 SQL의 FROM (JOIN)절에 영향을 주게 된다.

⭐ 묵시적 join은 안쓰는게 좋다. 그냥 명시적으로 join 시켜주는게 개발 과정에서 유지보수에 용이하다. ⭐

select [o.member.team](http://o.member.team) from Order o

→ 이렇게 활용하면 편하긴 하지만, 어느 테이블이 어떤 join column을 기준으로 join됐는지 알기 힘들어 유지보수에 어려움을 겪을 수 있다.

+조인은 SQL 튜닝에 매우 중요한 포인트인데, 묵시적 조인이 일어날 경우 조인이 일어나는 상황을 파악하기 어렵다.

⭐ 그냥 JPQL도 최대한 SQL 형식에 맞춰서 적는게 제일 깔끔하다. ⭐

profile
경험과 기록으로 성장하기
post-custom-banner

0개의 댓글