인프런 커뮤니티 질문&답변

임현강님의 프로필 이미지
임현강

작성한 질문수

자바 ORM 표준 JPA 프로그래밍 - 기본편

JPQL 타입 표현과 기타식

type(i) = Book 의 사용

작성

·

283

0

시간이 좀 지나서 자세히 기억은 안 나지만 이전 상속 관계 매핑 부분에서 TABLE_PER_CLASS 전략일 경우 Item만을 위한 테이블이 쓸 데 없이 생성되는 낭비를 막기 위해 어떠한 조치를 해주었던 기억이 납니다.
(abstract을 해줬던 거 같네요)

이같은 기억으로 미루어볼 때 dtype 속성을 사용해서 특정 자식 객체만 찾는 이 방식은 JOINED, SINGLE_TABLE 전략에서만 사용 가능할 거 같은데 제 추측이 맞을까요?

답변 2

6

이에 대한 결과가 궁금해서 '상속관계 매핑' 에서 진행했던 예제를 토대로 직접 실습을 진행해봤습니다.

다음은 Main 함수 코드입니다.

 try {

            Book book = new Book();
            book.setName("bookA");
            book.setAuthor("babo");
            em.persist(book);

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

            List<Item> result = em.createQuery("SELECT i FROM Item i WHERE TYPE(i) = Book", Item.class)
                    .getResultList();

            for (Item item : result) {
                Book book1 = (Book) item;
                System.out.println("book1.getName() = " + book1.getName());
                System.out.println("book1.getAuthor() = " + book1.getAuthor());
            }

            tx.commit();
} catch (Exception e) {
            tx.rollback();
            e.printStackTrace();
        } finally {
            em.close();
            emf.close();
        }
}

그런 다음 상속 전략을 JOIN, SINGLE_TALBE, TABLE_PER_CLASS으로 바꿔가며 쿼리 결과를 살펴봤습니다. (참고로 @DiscriminatorColumn 값은 기본값인 DTYPE, 나머지 테이블의 @DiscriminatorValue 값은 앞 글자의 대문자로 설정하였습니다.)

1. JOINED

LEFT JOIN을 하면서 ITEM의 DTYPE이 'B'인 조건을 만족하는 값을 찾는 SELECT 쿼리가 실행되는 것을 확인할 수 있었습니다.

Hibernate: 
    /* SELECT
        i 
    FROM
        Item i 
    WHERE
        TYPE(i) = Book */ select
            item0_.id as id2_5_,
            item0_.name as name3_5_,
            item0_.price as price4_5_,
            item0_1_.artist as artist1_1_,
            item0_2_.author as author1_2_,
            item0_2_.isbn as isbn2_2_,
            item0_3_.actor as actor1_9_,
            item0_3_.director as director2_9_,
            item0_.DTYPE as dtype1_5_ 
        from
            Item item0_ 
        left outer join
            Album item0_1_ 
                on item0_.id=item0_1_.id 
        left outer join
            Book item0_2_ 
                on item0_.id=item0_2_.id 
        left outer join
            Movie item0_3_ 
                on item0_.id=item0_3_.id 
        where
            item0_.DTYPE='B'
book1.getName() = bookA
book1.getAuthor() = babo

2. SINGLE_TABLE

SINGLE_TABLE 전략은 ITEM 테이블에 모든 값이 저장되므로 조인의 과정없이 ITEM의 DTYPE이 'B'인 조건을 만족하는 값을 찾는 SELECT 쿼리가 발생하는 것을 확인할 수 있었습니다.

Hibernate: 
    /* SELECT
        i 
    FROM
        Item i 
    WHERE
        TYPE(i) = Book */ select
            item0_.id as id2_3_,
            item0_.name as name3_3_,
            item0_.price as price4_3_,
            item0_.artist as artist5_3_,
            item0_.author as author6_3_,
            item0_.isbn as isbn7_3_,
            item0_.actor as actor8_3_,
            item0_.director as director9_3_,
            item0_.DTYPE as dtype1_3_ 
        from
            Item item0_ 
        where
            item0_.DTYPE='B'
book1.getName() = bookA
book1.getAuthor() = babo

3. TABLE_PER_CLASS

UNION을 통한 어마무시한 SELECT 쿼리가 발생한 것을 확인할 수 있었습니다. 그러나 위의 두 전략과는 다르게 DTYPE을 통해 만족하는 조건을 찾는 것이 아니라 'clazz_' 라는 속성을 통해 만족하는 조건을 찾는 것을 확인할 수 있었습니다. 이 clazz_는 TABLE_PER_CLASS 전략을 사용할 때만 생성되는 것 같았고, FROM 절의 서브 쿼리를 보면 Album, Book, Movie 테이블에 각각 clazz_의 값이 1, 2, 3으로 설정되는 것을 확인할 수 있었습니다. 

Hibernate: 
    /* SELECT
        i 
    FROM
        Item i 
    WHERE
        TYPE(i) = Book */ select
            item0_.id as id1_5_,
            item0_.name as name2_5_,
            item0_.price as price3_5_,
            item0_.artist as artist1_1_,
            item0_.author as author1_2_,
            item0_.isbn as isbn2_2_,
            item0_.actor as actor1_9_,
            item0_.director as director2_9_,
            item0_.clazz_ as clazz_ 
        from
            ( select
                id,
                name,
                price,
                artist,
                null as author,
                null as isbn,
                null as actor,
                null as director,
                1 as clazz_ 
            from
                Album 
            union
            all select
                id,
                name,
                price,
                null as artist,
                author,
                isbn,
                null as actor,
                null as director,
                2 as clazz_ 
            from
                Book 
            union
            all select
                id,
                name,
                price,
                null as artist,
                null as author,
                null as isbn,
                actor,
                director,
                3 as clazz_ 
            from
                Movie 
        ) item0_ 
    where
        item0_.clazz_=2
book1.getName() = bookA
book1.getAuthor() = babo

결론

JOINED, SINGLE_TABLE, TABLE_PER_CLASS 전략 모두 상속관계에서 TYPE 연산을 통해 특정 자식 클래스를 조회할 수 있었습니다. 그러나 JOINED, SINGLE_TABLE 전략은 DTYPE을 사용하고, TABLE_PER_CLASS는 clazz_ 라는 속성(?)을 사용합니다. 

실습 내용이 많이 부족하고 어설프기 때문에 잘못된 점이나 부족한 점 있으면 많이 조언 남겨주세요  😅

0

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. 현강님^^

제가 답을 바로 알려드릴 수도 있지만, 이런 부분은 스스로 한번 테스트를 해보면 더 많이 배우실 수 있습니다^^

그리고 테스트하고 결과를 남겨주시면 다른 분들에게도 도움이 될 거에요^^

감사합니다.

임현강님의 프로필 이미지
임현강
질문자

넵 답변 감사합니다.

강의의 코드를 따라치면서 듣다 보면 어느 순간부터는 그냥 기계적으로 타이핑하기에 급급할 뿐이고 내용에 대한 이해는 제대로 이뤄지지 않더라구요.

그래서 일단은 따라치지 않고 시청만 하면서 내용을 익힌 후 다시 한 번 돌려보면서 따라쳐보고 실습해보는 나름의 방식대로 학습을 진행하는 중입니다.

사실 직접 쳐보면 금방 알 수 있는 내용이란 걸 알면서도 이전부터 따라와놓은 코드가 없어서 질문을 드렸습니다;ㅎ

1회독(?)은 이제 거의 끝나가니 위 내용은 2회차 때에 실습해보도록 하겠습니다.

감사합니다^^

김영한님의 프로필 이미지
김영한
지식공유자

네^^ 화이팅!

임현강님의 프로필 이미지
임현강

작성한 질문수

질문하기