반응형

1. 개요

 - JPQL의 내부, 외부, 세타 조인에 대해 알아보자.


2. 내부 조인

 - 연관 관계가 맺어진 엔티티들에 대한 Inner Join을 말한다.

 - JPQL 작성 시 INNER JOIN의 INNER 는 생략 가능하다.

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

3. 외부 조인

 - 연관 관계가 맺어진 엔티티들에 대한 Left Outer Join을 말한다.

 - JPQL 작성 시 LEFT OUTER JOIN의 OUTER는 생략 가능하다.

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

4. 세타 조인

 - 엔티티들에 대한 조인을 말한다.

 - 연관 관계와 상관 없기에 엔티티 명을 정확히 기입해야 한다.

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

5. 조인 예제

 - 테스트를 위해 teamA와 teamB를 생성하였다. 멤버 0부터 9까지는 teamA, 멤버 10부터 19까지는 teamB에 속하도록 하였고, 멤버 20부터 29까지는 팀이 없도록 테스트 데이터를 세팅하였다. 데이터 셋 코드는 다음과 같다.

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

    Team teamB = new Team();
    teamB.setName("teamB");
    em.persist(teamB);

    for(int i =0 ; i< 10 ; i++){
        Member member = new Member();
        member.setUsername("멤버"+i);
        member.setAge(i);
        member.changeTeam(teamA);
        em.persist(member);
    }

    for(int i =10 ; i< 20 ; i++){
        Member member = new Member();
        member.setUsername("멤버"+i);
        member.setAge(i);
        member.changeTeam(teamB);
        em.persist(member);
    }

    for(int i =20 ; i< 30 ; i++){
        Member member = new Member();
        member.setUsername("멤버"+i);
        member.setAge(i);
        em.persist(member);
    }

 

5.1. 내부 조인 예제

 - 아래의 JPQL을 실행하여 내부 조인 시 team이 존재하는 Member 정보만을 리턴한다.

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

    for(Member member : list){
        System.out.println(member.toString());
        System.out.println(member.getTeam());
    }

	// 출력 결과
    Member{id=3, username='멤버0', age=0}
    Team{id=1, name='teamA'}
    Member{id=4, username='멤버1', age=1}
    Team{id=1, name='teamA'}
    ...
    Member{id=21, username='멤버18', age=18}
    Team{id=2, name='teamB'}
    Member{id=22, username='멤버19', age=19}
    Team{id=2, name='teamB'}

 

5.2. 외부 조인 예제

 - 아래의 JPQL을 실행하여 외부 조인 시 team이 존재하지 않는 Member 정보도 함께 리턴한다.

    String leftJoinQuery = "select m from Member m left join m.team t";
    List<Member> list2 = em.createQuery(leftJoinQuery,Member.class)
        .getResultList();

    for(Member member : list2){
        System.out.println(member.toString());
        System.out.println(member.getTeam());
    }

	// 출력 결과
    Member{id=3, username='멤버0', age=0}
    Team{id=1, name='teamA'}
    Member{id=4, username='멤버1', age=1}
    Team{id=1, name='teamA'}
    ...
    Member{id=31, username='멤버28', age=28}
    null
    Member{id=32, username='멤버29', age=29}
    null

 

5.3. 세타 조인 예제

 - 테스트를 위해 member 이름이 teamA인 멤버를 생성하였다. 실제 실행된 쿼리 확인 결과, 두 테이블을 크로스 조인 후 조건에 해당하는 값만을 조회한다.

    Member thetaMember = new Member();
    thetaMember.setUsername("teamA");
    thetaMember.changeTeam(teamA);
    em.persist(thetaMember);

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

    for(Member member : list3){
        System.out.println(member.toString());
        System.out.println(member.getTeam());
    }

    // 출력 결과
    Member{id=33, username='teamA', age=0}
    Team{id=1, name='teamA'}

6. 조인 대상 필터링

 - SQL에서 사용하던 on과 동일하게 사용한다. 내부 조인, 외부 조인도 동일한 방식으로 사용 가능하다.

    // 내부 조인에 대한 필터링 - 회원과 팀을 조인하면서, 팀 이름이 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'

    // 외부 조인에 대한 필터링 - 회원의 이름과 팀 이름이 같은 대상 외부조인
    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

7. 회고

 - JPQL에서 사용하는 조인과 SQL에서 사용하는 조인은 조인의 개념만 잘 알고있다면 어렵지 않게 적용함을 알았다. 모든 조인에 세타 조인을 사용해도 되나, 크로스 조인으로 인한 성능 저하를 고려해봤을 때 테이블의 연관관계를 확실히 이해하고 그에 따른 조인 전략을 구상하는 게 중요함을 느꼈다.

 


8. 참고

 - JAVA ORM 표준 JPA 프로그래밍 - 김영한

반응형

+ Recent posts