[Item]
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn
public class Item {
@Id @GeneratedValue
private Long id;
private String name;
private int price;
}
[Album]
@Entity
@DiscriminatorValue("ALBUM")
public class Album extends Item {
private String artist;
}
[Movie]
@Entity
@DiscriminatorValue("MOVIE");
public class Movie extends Item {
private String director;
private String actor;
}
[JpaMain]
Movie movie = new Movie();
movie.setDirector("aaaaa");
movie.setActor("bbbb");
movie.setName("바람과 함께 사라지다");
movie.setPrice(10000);
em.persist(movie);
외래키 값은 NULL이거나 참조 테이블의 기본키 값과 동일해야 한다는 것이다.
즉 참조할 수 없는 외래키 값을 가질 수 없다는 것이다.
ex) 수강
테이블의 학번
속성에는 학생
테이블의 학번
속성에 없는 값을 입력할 수 없다.
무결성 제약조건 관련 내용은 무결성 제약조건를 참고하길 바란다.
조인테이블 전략의 단점으로
라고 설명이 되어있는데 그냥 그런갑다... 하고 넘어간 내 자신을 반성한다 ㅎㅎ.
JPA 스터디원 중 한명인 핑구님이 상속관계 매핑의 문제점(이 포스트를 한번 읽어보는 걸 추천한다)에서 조인테이블 전략의 단점을 자세히 설명하였고, 이에 대해 간단히 정리하고자 한다.
@Test
@DisplayName("상속관계 매핑 조회 테스트")
public void findById() throws Exception {
// given
Movie movie = Movie.builder()
.name("바람과 함께 사라지다")
.actor("ppak")
.price(10000)
.director("mrPPak")
.build();
Album album = Album.builder()
.name("명곡 앨범")
.artist("mrPPak")
.price(100000)
.build();
movieRepository.save(movie);
albumRepository.save(album);
// when
Item item1 = itemRepository.findById(1L).get();
}
위와 같이 테스트코드를 작성한 후 실행해보니 다음과 같은 쿼리가 호출되었다.
select
item0_.id as id2_5_0_,
item0_.name as name3_5_0_,
item0_.price as price4_5_0_,
item0_1_.artist as artist1_0_0_,
item0_2_.actor as actor1_6_0_,
item0_2_.director as director2_6_0_,
item0_.dtype as dtype1_5_0_
from
item item0_
left outer join
album item0_1_
on item0_.id=item0_1_.id
left outer join
movie item0_2_
on item0_.id=item0_2_.id
where
item0_.id=?
보다시피 join이 2번 호출됨을 알 수 있다.
그래서 나는 join하지 않고 그냥 Item 테이블만 검색하기 위해 다음과 같이 작성했다.
// queryDsl
@Override
public Optional<Item> findByIdCustom(Long id) {
return Optional.ofNullable(
queryFactory.selectFrom(item).where(item.id.eq(id)).fetchOne());
}
Item item2 = em.createQuery("select i from Item i where i.id = 1", Item.class)
.getSingleResult();
Item item3 = itemRepository.findByIdCustom(1L).get();
그리고 테스트를 진행하였는데도 불구하고 위의 쿼리와 같은 쿼리가 호출되는것이 아니겠는가?
응 뭐지? join 안쓰도록 강제했는데....?????
답은 간단하였다... (이럴 때마다 바보같음을 느낀다 ㅋㅋㅋ)
ItemRepository
의 findById()
도 결국 jpql로 구성된 것인데,
itemRepository.findById()
를 호출하든,
em.createQuery("select i from Item i where i.id = 1", Item.class)
를 호출하든
결국 똑같은 jpql을 호출한 것이라는 것을 알게되었다...
정리하면 분명히 select i from Item i where i.id = 1
이라는 jpql을 호출하였으나
하이버네이트에서 이 jpql을 보고 join절까지 추가하여 쿼리를 내보내는 것이었다...
난 join절 쓰기 싫다고!!!
@Inheritance 를 쓰지말고 직접 OneToOne으로 구현하는 방법이 있고,
최후의 방법으로 마이바티스
같은 것을 이용하는 방법이 있다.
이에 대한 내용도 상속관계 매핑의 문제점에 있으니 참고하길 바란다.