89일차 - JPA (join, 서브쿼리)

Yohan·2024년 7월 1일
0

코딩기록

목록 보기
131/156
post-custom-banner

JOIN

Inner Join 메서드

  • innerJoin()은 두개의 파라미터를 받음
  1. 조인할 대상 엔터티의 경로
    from에서 갖고 있는 FK
  2. 조인 조건에 사용될 엔터티 (on절에 들어가야 하는 내용)
    조인할 테이블
    @Test
    @DisplayName("내부 조인 예제")
    void innerJoinTest() {
        //given
        List<Tuple> idolList = factory
                .select(idol, group)
                .from(idol)
                // QueryDsl은 join할때 on절을 쓰지않는다.
                // innerJoin에서 파라미터를 2개넣어 한번에 해결
                // 첫번째 파라미터는 from절에 있는 엔터티의 연관 객체
                // 두번째 파라미터는 실제로 조인할 엔터티클래스
                .innerJoin(idol.group, group)
                .fetch();
    }
  • Inner Join 2번
    
    @Test
    @DisplayName("2022년에 발매된 앨범이 있는 아이돌의 이름과 그룹명과 앨범명과 발매년도 조회")
    void selectAlbumReleasedYear() {
        //given
        int year = 2022;
        //when
        List<Tuple> list = factory
                .select(idol.idolName, group.groupName, album.albumName, album.releaseYear)
                .from(idol)
                .innerJoin(idol.group, group)
                .innerJoin(group.albums, album)
                .where(album.releaseYear.in(year))
                .fetch();
        //then
        for (Tuple tuple : list) {
            String name = tuple.get(idol.idolName);
            String gName = tuple.get(group.groupName);
            String aName = tuple.get(album.albumName);
            int releaseYear = tuple.get(album.releaseYear);
            System.out.println("Idol: " + name + ", Group: " + gName + ", Album: " + aName + ", ReleaseYear: " + releaseYear);
        }
    } 

Outer Join 메서드


    @Test
    @DisplayName("Left Outer Join")
    void outerJoinTest() {

        //when
        List<Tuple> result = factory
                .select(idol, group)
                .from(idol)
                .leftJoin(idol.group, group)
                .fetch();
        //then
        assertFalse(result.isEmpty());
        for (Tuple tuple : result) {
            Idol i = tuple.get(idol);
            Group g = tuple.get(group);

            System.out.println("\nIdol: " + i.getIdolName()
                    + ", Group: "
                    + (g != null ? g.getGroupName() : "솔로가수"));
                    // nvl 처리
        }
    }

nvl을 꼭 사용해야 할때

  • QueryDsl에서는 nvl, rollup, cube, 윈도우함수 등을 지원하지 않아
    결국에는 native query를 써야한다.
  • IdolRepositoryImpl
public class IdolRepositoryImpl implements IdolCustomRepository {

    private final JdbcTemplate template;
    
    private final EntityManager em;

    private final JPAQueryFactory factory;

    // native query 사용 (nvl)
    public void nativeQuery123() {
        String sql = "SELECT " +
                "I.idol_id, NVL(G.group_id, '솔로가수') AS g_id" +
                "FROM tbl_idol I" +
                "LEFT JOIN tbl_group G" +
                "ON I.group_id = G.group_id";
    }
    

서브쿼리

  • subQuery는 'JPAExpressions' 객체를 사용
  • JPAExpressions.select()해서 넣으면 됨.
    @Test
    @DisplayName("특정 그룹의 평균 나이보다 많은 아이돌 조회")
    void subQueryTest1() {
        // subQuery는 'JPAExpressions' 객체를 사용한다.

        //when
        List<Idol> result = factory
                .select(idol)
                .from(idol)
                .where(idol.age.gt(
                        JPAExpressions
                                .select(idol.age.avg())
                                .from(idol)
                                .innerJoin(idol.group, group)
                                .where(group.groupName.eq("르세라핌"))
                    ).and(idol.group.isNotNull())
                )
                .fetch();
  • 연관 서브쿼리에서 하나의 테이블에 다른 별칭을 사용해야 할 경우.
    new Qtype으로 생성해서 해결

    @Test
    @DisplayName("그룹별 가장 최근에 발매된 앨범을 조회")
    void subQueryTest2() {

        /*          그룹별이니 결과 행수가 4
            SELECT G.group_name, A.album_name, A.release_year
            FROM tbl_group G
            INNER JOIN tbl_album A
            ON G.group_id = A.group_id
            WHERE A.album_id IN (
                    SELECT S.album_id
                    FROM tbl_album S
                    WHERE S.group_id = A.group_id
                         AND (
                            SELECT MAX(release_year)
                            FROM tbl_album
                            WHERE S.group_id = A.group_id
                         )
            )
         */

        //given
        // subQuery시 alias 충돌 방지
        QAlbum albumA = new QAlbum("albumA");
        QAlbum albumS = new QAlbum("albumS");

        //when
        List<Tuple> list = factory
                .select(group.groupName, albumA.albumName, albumA.releaseYear)
                .from(group)
                .innerJoin(group.albums, albumA)
                .where(albumA.id.in(
                        JPAExpressions
                                .select(albumS.id)
                                .from(albumS)
                                .where(albumS.group.id.eq(albumA.group.id)
                                        .and(albumS.releaseYear.eq(
                                                JPAExpressions
                                                        .select(albumS.releaseYear.max())
                                                        .from(albumS)
                                                        .where(albumS.group.id.eq(albumA.group.id))
                                        ))
                                )
                ))
                .distinct() // 중복 제거
                .fetch();

        //then

    }
profile
백엔드 개발자
post-custom-banner

0개의 댓글