JPA 객체지향 쿼리 언어(JPQL) - 조인(JOIN)

2022. 7. 17. 21:27·공부/JPA
반응형

출처 : 자바 ORM 표준 JPA 프로그래밍 인프런 강의

 

JPA 게시글은 대부분 인프런의 김영한님의 강의인 '자바 ORM 표준 JPA 프로그래밍' 기반으로 내용을 정리했습니다.

 

조인(JOIN)

내부 조인(INNER JOIN)

SELECT m FROM Member m [INNER] JOIN m.team t

JPQL 조인은 엔티티 중심으로 동작을 한다. Member와 연관있는 Team을 m.team과 같이 점(.)으로 표현하고, 별칭을 t로 준다. 

참고로 내부 조인은 Member는 있는데 team 데이터가 없을 경우에 Member만 나온다.

// 실행 클래스
for(int i = 0; i < 100; i++) {

    Team team = new Team();
    team.setName("teamA");
    em.persist(team);

    Member member = new Member();
    member.setUsername("member" + i);
    member.setAge(10);
    member.setTeam(team);
    em.persist(member);
}

em.flush();
em.clear();

String query = "select m from Member m inner join m.team t";
List<Member> result = em.createQuery(query, Member.class)
        .getResultList();

 

Member와 Team의 양방향 연관관계를 위해 Member 클래스에 연관 관계 편의 메서드를 작성한다.

public void changeTeam(Team team) {
    this.team = team;
    team.getMembers().add(this);
}

그리고 실행해보자.

Hibernate: 
    /* select
        m 
    from
        Member m 
    inner join
        m.team t */ select
            member0_.id as id1_0_,
            member0_.age as age2_0_,
            member0_.TEAM_ID as TEAM_ID4_0_,
            member0_.username as username3_0_ 
        from
            Member member0_ 
        inner join
            Team team1_ 
                on member0_.TEAM_ID=team1_.id
Hibernate: 
    select
        team0_.id as id1_3_0_,
        team0_.name as name2_3_0_ 
    from
        Team team0_ 
    where
        team0_.id=?

위와 같이  JOIN 외에 다른 SELECT 쿼리가 추가적으로 나간 것을 볼 수 있다.  이 부분은 지연 로딩 설정으로 해결할 수 있다.

//Member.class 중 일부
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "TEAM_ID")
private Team team;

// 출력 로그
Hibernate: 
    /* select
        m 
    from
        Member m 
    inner join
        m.team t */ select
            member0_.id as id1_0_,
            member0_.age as age2_0_,
            member0_.TEAM_ID as TEAM_ID4_0_,
            member0_.username as username3_0_ 
        from
            Member member0_ 
        inner join
            Team team1_ 
                on member0_.TEAM_ID=team1_.id

 

참고로 JPQL의 inner 키워드는 생략이 가능하다.

String query = "select m from Member m join m.team t";

 

 

외부 조인(OUTER JOIN)

SELECT m FROM Member m LEFT [OUTER] JOIN m.team t

외부조인은 Member만 있고  Team이 없는 경우에 Team을 다 NULL로 채워줘 Member와 Team이 같이 나오게 된다.

 

String query = "select m from Member m left outer join m.team t";

쿼리는 위의 예제에서 이 부분만 수정해주면 된다.

// 출력 로그

Hibernate: 
    /* select
        m 
    from
        Member m 
    left outer join
        m.team t */ select
            member0_.id as id1_0_,
            member0_.age as age2_0_,
            member0_.TEAM_ID as TEAM_ID4_0_,
            member0_.username as username3_0_ 
        from
            Member member0_ 
        left outer join
            Team team1_ 
                on member0_.TEAM_ID=team1_.id

 

 

세타 조인

SELECT COUNT(m) FROM Member m, Team t WHERE m.username = t.name

세타 조인의 경우 Member와 Team을 카티션 곱으로하고 where절에 조건을 걸어주는 것이다.

세타 조인의 쿼리는 아래와 같이  from 절에 객체를 쭉 써주고 where절에 조건을 걸어주면 된다.

String query = "select m from Member m, Team t where m.username = t.name";

 

맞게 실행됐나를 확인하기 위해 이상하지만 예제를 조금 손 보고 실행해보자.

for(int i = 0; i < 100; i++) {

    Team team = new Team();
    team.setName("teamA");
    em.persist(team);

    Member member = new Member();
    member.setUsername("teamA"); //teamA로 변경
    member.setAge(10);
    member.setTeam(team);
    em.persist(member);
}

em.flush();
em.clear();

String query = "select m from Member m, Team t where m.username = t.name";
List<Member> result = em.createQuery(query, Member.class)
        .getResultList();

System.out.println("result.size() = " + result.size());
// 출력 로그
Hibernate: 
    /* select
        m 
    from
        Member m,
        Team t 
    where
        m.username = t.name */ select
            member0_.id as id1_0_,
            member0_.age as age2_0_,
            member0_.TEAM_ID as TEAM_ID4_0_,
            member0_.username as username3_0_ 
        from
            Member member0_ cross // cross
        join
            Team team1_ 
        where
            member0_.username=team1_.name
result.size() = 10000

 

 

조인 - ON 절

  • ON절을 활용한 조인(JPA 2.1부터 지원)
    • 조인 대상 필터링
    • 연관관계 없는 엔티티 외부 조인(하이버네이트 5.1부터)

 

조인 대상 필터링

  • ex) 회원과 팀을 조인하면서, 팀 이름이 A인 팀만 조인
  • JPQL
SELECT m, t FROM Member m LEFT JOIN m.team t on t.name = 'A'
  • SQL
SELECT m.*, t.* FROM
Member m LEFT JOIN Team t ON m.TEAM_ID = t.id and t.name = 'A'
  • 예제
String query = "select m from Member m left join m.team t on t.name = 'teamA'";
List<Member> result = em.createQuery(query, Member.class)
        .getResultList();
        

// 출력 로그
Hibernate: 
    /* select
        m 
    from
        Member m 
    left join
        m.team t 
            on t.name = 'teamA' */ select
                member0_.id as id1_0_,
                member0_.age as age2_0_,
                member0_.TEAM_ID as TEAM_ID4_0_,
                member0_.username as username3_0_ 
        from
            Member member0_ 
        left outer join
            Team team1_ 
                on member0_.TEAM_ID=team1_.id  // **ON절
                and (
                    team1_.name='teamA'
                )

 

 

연관관계가 없는 엔티티 외부 조인

  • ex) 회원의 이름과 팀의 이름이 같은 대상 외부 조인
  • JPQL
SELECT m, t FROM
Member m LEFT JOIN Team t on m.username = t.name
  • SQL
SELECT m.*, t.* FROM
Member m LEFT JOIN Team t ON m.username = t.name
  • 예제
String query = "select m from Member m left join Team t on m.username = t.name";
List<Member> result = em.createQuery(query, Member.class)
        .getResultList();
        
// 출력 로그
Hibernate: 
    /* select
        m 
    from
        Member m 
    left join
        Team t 
            on m.username = t.name */ select
                member0_.id as id1_0_,
                member0_.age as age2_0_,
                member0_.TEAM_ID as TEAM_ID4_0_,
                member0_.username as username3_0_ 
        from
            Member member0_ 
        left outer join
            Team team1_ 
                on ( // ** ON절
                    member0_.username=team1_.name
                )

 

springboot 버전이 1.X 대면 하이버네이트 버전이 5.1이 아니라 안 될 수도 있다.

 

반응형
저작자표시 비영리 변경금지 (새창열림)
'공부/JPA' 카테고리의 다른 글
  • JPA 객체지향 쿼리 언어(JPQL) - JPQL 타입 표현과 기타식
  • JPA 객체지향 쿼리 언어(JPQL) - 서브 쿼리
  • JPA 객체지향 쿼리 언어(JPQL) - 페이징 API
  • JPA 객체지향 쿼리 언어(JPQL) - 프로젝션(SELECT)
데부한
데부한
어차피 할 거면 긍정적으로 하고 싶은 개발자
    반응형
  • 데부한
    동동이개발바닥
    데부한
  • 전체
    오늘
    어제
    • 분류 전체보기 (307)
      • 방통대 컴퓨터과학과 (27)
        • 잡담 (9)
        • 3학년1학기 (17)
      • 프로젝트 및 컨퍼런스 회고 (1)
        • 프로젝트 (4)
        • 한이음 프로젝트 (0)
        • 회고 (3)
      • 공부 (165)
        • Spring (37)
        • JPA (71)
        • 인프런 워밍업 클럽_BE (10)
        • Java (6)
        • React.js (27)
        • 넥사크로 (11)
        • 기타 (3)
      • 알고리즘 (85)
        • 알고리즘 유형 (10)
        • 알고리즘 풀이 (57)
        • SQL 풀이 (18)
      • 에러 해결 (13)
      • 잡담 (7)
        • 국비교육 (2)
        • 구매후기 (5)
        • 진짜 잡담 (0)
  • 블로그 메뉴

    • Github
    • Linkedin
    • 홈
    • 방명록
    • 글쓰기
    • 관리
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    QueryDSL
    토이프로젝트
    egov
    넥사크로
    IT
    코딩테스트
    SpringBoot를 이용한 RESTful Web Service 개발
    MSA
    자바스크립트
    SQL
    Spring
    oracle
    운영체제
    스프링부트
    react
    에러해결
    RESTful
    기출문제
    전자정부프레임워크
    프로그래머스
    알고리즘
    springboot
    개발자
    인프런
    방통대
    백준
    Java
    프론트엔드
    토비의스프링부트
    JPA
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
데부한
JPA 객체지향 쿼리 언어(JPQL) - 조인(JOIN)
상단으로

티스토리툴바