본문 바로가기
JPA

JPA 조인&서브쿼리

by 이상한나라의개발자 2024. 3. 12.

조인

JPA에서 조인은 SQL 조인과 개념이 유사하며, 여러 테이블의 데이터를 결합하여 정보를 조회하는 방법입니다. 크게 내부 조인(Inner Join), 외부 조인(Inner Join), 세타 조인(Theta Join)을 사용할 수 있습니다. 이들 각각에 대해서 Member, Team 엔티티 기준으로 작성하겠습니다.

@Entity
@Getter
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;

    @ManyToOne
    @JoinColumn(name = "team_id")
    private Team team;
}
@Entity
@Getter
public class Team {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
}

 

내부 조인 (Inner Join)

내부 조인은 두 테이블에서 조건이 일치하는 데이터만을 결과로 반환합니다. JPA에서 JOIN 이나 INNER JOIN 키워드를 사용하여 내부 조인을 할 수 있습니다.

 

JPQL을 사용한 내부 조인 예시이며, 특정 팀 이름(teamName)을 가진 Member 와 Team을 조인 합니다.

SELECT m FROM Member m JOIN m.team t WHERE t.name = :teamName

 

외부 조인 (Outer Join)

외부 조인은 조건이 일치하지 않는 데이터도 포함하여 결과를 반환합니다. JPA에서 LEFT JOIN, RIGHT JOIN, FULL JOIN 을 사용하여 외부 조인을 수행할 수 있으니, 대부분의 경우 LEFT JOIN이 가장 흔하게 사용됩니다.

 

JPQL을 사용한 외부 조인 예시이며, Member와 Team을 외부 조인하여, 팀에 속하지 않는 회원들도 결과에 포함 합니다.

SELECT m FROM Member m LEFT JOIN m.team t

 

세타 조인 (Theta Join)

세타 조인은 연관관계가 없는 두 엔티티 간의 조인을 말합니다. JPA에서 CROSS JOIN을 사용하여 세타 조인을 구현할 수 있으며 ON 구문을 사용하여 조인 조건을 명시할 수 있습니다.

 

JPQL을 사용한 세타 조인 예시이며, Member와 Team 간에 직접적인 연관관계가 없을 때 사용되며, 사용자 이름과 팀 이름이 같은 경우를 조회합니다.

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

 

 

각 유형은 사용하는 상황과 필요에 따라 선택하여 사용할 수 있습니다. 내부 조인은 가장 흔히 사용되는 조인유형이며, 두 테이블 간에 일치하는 데이터가 필요할 때 사용됩니다. 외부 조인은 한 테이블의 데이터가 다른 테이블과 일치하지 않아도 결과에 포함시키고 싶을 때 유용합니다. 세타 조인은 두 테이블의 연관관계가 없을 때 사용됩니다.

 

서브 쿼리

서브 쿼리는 sql 문 내에서 다른 SQL 문을 중첩하여 사용하는 것을 말합니다. 서브쿼리는 SELECT, INSERT, UPDATE 또는 DELETE 문 내에서 사용될 수 있으며, 하나의 SQL 문이 다른 SQL 문의 결과를 이용해야할 때 유용합니다. 참고로, 서브쿼리는 성능 저하의 원인이 될 수 있으므로, 사용할 때는 성능 테스트와 최적화를 고려해야 합니다.

 

서브쿼리의 사용 용도

  • WHERE 절에서 특정 조건을 만족하는 데이터를 필터링하는 데 사용됩니다.
  • SELECT 절에서 컬럼 겂을 계산하는데 사용됩니다.
  • FROM 절에서 동적으로 테이블을 생성하여 사용하는 데 사용됩니다.

다음은 Member, Team 엔티티 N:1 관계일때의 사용 예시입니다. 두 엔티티는 위 조인 섹션에서 작성한 코드 기준 입니다.

 

WHERE 절에서 사용하는 서브쿼리

특정 팀에 속한 모든 멤버의 이름을 조회하는 쿼리입니다. 이 서브쿼리는 TeamA인 팀의 ID를 찾고 이 ID를 사용해 해당 팀에 속한 Member 를 조회합니다.

SELECT m.username FROM Member m WHERE m.team_id IN (SELECT t.id FROM Team t WHERE t.name = 'TeamA')

@Query("select m.username " +
        " from Member m " +
        "where m.team.id in (select t.id from Team t where t.name = :teamName)")
List<String> findMember(@Param("teamName") String teamName);

 

SELECT 절에서 사용되는 서브쿼리

각 팀의 멤버 수를 조회하는 쿼리입니다. 이 쿼리는 Team에 대해 Mmeber 테이블에서 해당 팀 ID와 일치하는 멤버 수를 조회 합니다.

@Query("select t.name" +
        "    , (select count(*) from Member m where m.team.id = t.id) AS member_count " +
        " from Team t")
List<Map<String, Object>> findTeamMemberCount();

 

FROM 절에서 사용하는 서브쿼리

JPQL에서 from 절에 서브쿼리를 사용은 것은 허용되지 않습니다. 그러므로 JQPL 에서 네이티브 쿼리로 작성을 해야 합니다.

아래는 특정 조건을 만족하는 멤버들의 평균 연령을 계산하는 쿼리입니다. 여기에서 FROM 절에 서브쿼리를 사용합니다.

네이티브 쿼리를 사용하기 위해서는 nativeQuery = true 옵션이 있어야 하며 , 실제 DB의 테이블과 속성이 일치해야 합니다.

쉽게 말해 oracle라면 oracle 쿼리를 그대로 사용하신다고 보면 됩니다.

@Query(value = "select avg(m.age) " +
        "         from ( " +
        "                select m.age " +
        "                  from member m inner join team t " +
        "                    on m.team_id = t.team_id " +
        "                 where t.name = 'teamA' " +
        "               ) as m", nativeQuery = true)
List<Double> findAverageAgeOfTeamA();

'JPA' 카테고리의 다른 글

Spring Data JPA  (0) 2024.03.08
JPA 조회 전략  (0) 2024.03.07
JPA Entity 설계시 베스트 프랙티스  (0) 2024.02.22
JPA OSIV  (0) 2024.01.31
JPA Auditing  (0) 2024.01.30