- 참조: 자바 ORM 표준 JPA 프로그래밍
- 다형성 쿼리
앞에서 JPA 상속관계 맵핑을 배운적이 있다. Item 엔티티를 확장하여 Book, Album, Movie 엔티티를 선언하였다. Single Table 전략으로 선언했던 Item 과 Book 엔티티의 코드를 다시 한번 살펴보자.
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "ITEM_TYPE")
public abstract class Item extends DateMarkable {
@Id
@GeneratedValue
@Column(name = "ITEM_ID")
private Long id;
private String name;
private int price;
private int stockQuantity;
............
@Entity
@DiscriminatorValue("BOOK")
public class Book extends Item{
private String author;
private String isbn;
만약 위와 같이 상속 관계 맵핑을 한 상태에서 JPQL 로 Item 엔티티를 조회하면 어떻게 될까?
Book book = new Book();
book.setName("Book#1");
book.setAuthor("Author#1");
em.persist(book);
Movie movie = new Movie();
movie.setName("Movie#1");
movie.setDirector("Director#1");
em.persist(movie);
Book 과 Movie 엔티티를 선언하여 영속화한 후 Item 엔티티를 조회하는 아래 JPQL 을 수행시켜보자.
String jpql = "select i "
+ "from Item i ";
List<Item> items = em.createQuery(jpql, Item.class)
.getResultList();
items.forEach(item -> {
System.out.println("Item name: " + item.getClass().getSimpleName());
});
위의 JPQL 을 실행하면 변환되는 SQL 과 결과는 아래와 같다. Item 엔티티를 상속받는 자식의 엔티티들을 모두 조회하는것을 알 수 있다.
Hibernate:
/* select
i
from
Item i */ select
item0_.item_id as item_id2_3_,
item0_.insert_datetime as insert_d3_3_,
item0_.update_datetime as update_d4_3_,
item0_.name as name5_3_,
item0_.price as price6_3_,
item0_.stock_quantity as stock_qu7_3_,
item0_.artist as artist8_3_,
item0_.author as author9_3_,
item0_.isbn as isbn10_3_,
item0_.actor as actor11_3_,
item0_.director as directo12_3_,
item0_.item_type as item_typ1_3_
from
item item0_
Item name: Book
Item name: Movie
만약 전략을 아래와 같이 JOINED 로 변경하면 수행되는 SQL 도 달라진다.
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "ITEM_TYPE")
public abstract class Item extends DateMarkable {
실행되는 SQL 을 보면 join 으로 변경된다.
Hibernate:
/* select
i
from
Item i */ select
item0_.item_id as item_id2_5_,
item0_.insert_datetime as insert_d3_5_,
item0_.update_datetime as update_d4_5_,
item0_.name as name5_5_,
item0_.price as price6_5_,
item0_.stock_quantity as stock_qu7_5_,
item0_1_.artist as artist1_0_,
item0_2_.author as author1_1_,
item0_2_.isbn as isbn2_1_,
item0_3_.actor as actor1_7_,
item0_3_.director as director2_7_,
item0_.item_type as item_typ1_5_
from
item item0_
left outer join
album item0_1_
on item0_.item_id=item0_1_.item_id
left outer join
book item0_2_
on item0_.item_id=item0_2_.item_id
left outer join
movie item0_3_
on item0_.item_id=item0_3_.item_id
Item name: Book
Item name: Movie
- TYPE
특정 Type 의 엔티티만 조회하고 싶다면 type() 함수를 사용할 수 있다.
String jpql = "select i "
+ "from Item i "
+ "where type(i) in (Book)";
List<Item> items = em.createQuery(jpql, Item.class)
.getResultList();
items.forEach(item -> {
System.out.println("Item name: " + item.getClass().getSimpleName());
});
Item 엔티티중에 Book 엔티티만 조회하고 싶다면 type([alias]) 로 비교할 엔티티 이름을 기술해준다.
Hibernate:
/* select
i
from
Item i
where
type(i) in (
Book
) */ select
item0_.item_id as item_id2_5_,
item0_.insert_datetime as insert_d3_5_,
item0_.update_datetime as update_d4_5_,
item0_.name as name5_5_,
item0_.price as price6_5_,
item0_.stock_quantity as stock_qu7_5_,
item0_1_.artist as artist1_0_,
item0_2_.author as author1_1_,
item0_2_.isbn as isbn2_1_,
item0_3_.actor as actor1_7_,
item0_3_.director as director2_7_,
item0_.item_type as item_typ1_5_
from
item item0_
left outer join
album item0_1_
on item0_.item_id=item0_1_.item_id
left outer join
book item0_2_
on item0_.item_id=item0_2_.item_id
left outer join
movie item0_3_
on item0_.item_id=item0_3_.item_id
where
item0_.item_type in (
'BOOK'
)
Item name: Book
실행되는 SQL 을 보면 Item 엔티티에서 @DiscriminatorColumn 로 지정한 ITEM_TYPE 컬럼에 Book 엔티티에서 지정한 @DiscriminatorValue 값인 BOOK 으로 조회하는것을 확인할 수 있다.
- TREAT
TYPE 으로 특정 엔티티를 조회하는것을 넘어서 특정 엔티티의 속성으로 조회조건을 주고 싶다면 treat() 을 사용한다.
String jpql = "select i "
+ "from Item i "
+ "where treat(i as Book).author = 'Author#1'";
List<Item> items = em.createQuery(jpql, Item.class)
.getResultList();
items.forEach(item -> {
System.out.println("Item name: " + item.getClass().getSimpleName());
});
위의 JPQL 은 마치 타입 캐스팅처럼 Item 엔티티의 alias 인 i 를 treat(i as Book) 문법을 사용하여 Book 엔티티로 처럼 변환하였다. Book 엔티티에만 있는 author 속성의 값이 'Author#1' 인 Book 엔티티만 조회한다.
Hibernate:
/* select
i
from
Item i
where
treat(i as Book).author = 'Author#1' */ select
item0_.item_id as item_id2_5_,
item0_.insert_datetime as insert_d3_5_,
item0_.update_datetime as update_d4_5_,
item0_.name as name5_5_,
item0_.price as price6_5_,
item0_.stock_quantity as stock_qu7_5_,
item0_1_.artist as artist1_0_,
item0_2_.author as author1_1_,
item0_2_.isbn as isbn2_1_,
item0_3_.actor as actor1_7_,
item0_3_.director as director2_7_,
item0_.item_type as item_typ1_5_
from
item item0_
left outer join
album item0_1_
on item0_.item_id=item0_1_.item_id
inner join
book item0_2_
on item0_.item_id=item0_2_.item_id
left outer join
movie item0_3_
on item0_.item_id=item0_3_.item_id
where
item0_2_.author='Author#1'
Item name: Book
실행된 SQL 을 보면 book 테이블만 inner join 으로 되어있다. 이는 treat 을 이용하면 해당 엔티티만 조회하기 때문이다. 만약 싱글테이블 전략으로 조회했다면 i.ITEM_TYPE = 'BOOK' 과 같은 조건이 where 절에 추가된다.
'Framework and Tool > JPA' 카테고리의 다른 글
JPA - 객체지향 쿼리 언어 - Criteria (0) | 2021.08.04 |
---|---|
JPA - 객체지향 쿼리 언어 - JPQL NamedQuery (0) | 2021.08.03 |
JPA - 객체지향 쿼리 언어 - JPQL 서브쿼리, 조건식 (0) | 2021.08.03 |
JPA - 객체지향 쿼리 언어 - JPQL 경로 표현식 (0) | 2021.08.02 |
JPA - 객체지향 쿼리 언어 - JPQL Fetch Join (0) | 2021.07.31 |
댓글