출처 - 김영한님의 강의를 보고 정리하였습니다.
이번에는 상속관계를 매핑해볼 것이다.
3가지의 방법이 있다.
1. 조인 전략
2. 단일 테이블 전략
3. 구현 클래스마다 테이블 전략
밑의 album 과 movie 자바 파일이다. Entity는 붙여주고 상위인 Item 을 extends로 상속 받는다.
Item class 이다. @Inheritance(strategy=InheritanceType.JOINED) 를 기입하면 조인 방식이 된다.
조인 방식은 위를 목표로 한다.
다음과 같이 new movie 후에 dir act name price를 기입하고 넣는다.
다음과 같이 나오게 된다. DTYPE은 ITEM 테이블에서 밑의 놈들이 누구인지 알려주는 타입인데,
위의 코드를 보면 @DiscriminatorColumn을 달아줘서 생긴 것이다. 이것만 달면 DTYPE에 밑의 클래스 명인 album movie book 이 나올 것이다.
다시 한 번 위의 코드를 보면 movie 에 @DiscriminatorValue("A") 라고 달아져있는 것을 볼 수 있을 것이다. 이 때문에 db에 A 로 들어 갔다.
단일 테이블은 다음과 같이 모든 것을 이렇게 짬뽕으로 다 섞은 것이다.
단일 테이블은 DiscriminalColumn 없이도 당연히 DTYPE이 생길 수 밖에 없다.
Inheritance 어노테이션 안에 JOINED 대신 SINGLE_TABLE이라고 수정하면 이렇게 된다.
다음은 TABLE_PER_CLASS라고 적어준 구현 클래스마다 테이블 전략이다.
다음과 같은 것을 의도한다.
item 테이블은 사라져있다. 실제로 위에 코드를 보면 abstract를 추가한 것을 볼 수가 있다.
조인 전략의 장점은
1. 테이블 정규화
2. 외래 키 참조 무결성 제약조건 활용가능(쉽게 말해 그냥 주테이블에서 여러 처리 가능)
3. 저장공간 효율화
단점
1. 조회 쿼리 시에 조인을 많이 사용하고 복잡해서 성능 저하
2. 데이터 저장 시에 insert 문을 2번 호출한다.
단일 테이블 전략의 장점은
1. 조인이 필요가 없어 조회 성능이 빠르고 쿼리가 단순하다.
단점은 자식 엔티티가 매핑한 컬럼은 모두 null을 허용한다는 점과 테이블이 커져 상황에 따라서 조회 성능이 오히려 느려질 수도 있다는 점이 있다.
구현 클래스마다 테이블 전략은 그냥 쓰지 말자.
이제 MappedSuperclass를 해보자.
다음과 같이 공통부분이 있을 때에 한 쪽에 빼내기 위해 사용한다.
다음과 같이 어노테이션 MapperSuperclass를 달아둔다. 위에선 class 로 했지만 abstract을 붙여주는 것이 좋다.
extends로 받아온다.
다음과 같은 결과를 얻게된다.
MappedSuperclass 는
상속관계 매핑 X, 엔티티 X, 테이블과 매핑 X, 자식 클래스에 매핑 정보만 제공하고, 조회 검색이 불가능하다(find 등).
직접 생성해서 사용할 일이 없으므로 추상 클래스를 권장한다.
실습 pjt
이번에 수행할 것은 다음과 같다. 등록일 수정일을 MappedSuperclass 하고, 상품의 종류를 상속관계 매핑을 하면 될 것이다.
도서,음반,영화를 joined 와 single_table를 둘 다 해봤고, baseentity 로 나머지에 적용시켜서 다 성공적으로 되었다.
실습 pjt end
프록시는 가짜이다.
em.find() 시에 db를 통해서 실제 엔티티 객체를 조회한다.
em.getReference() 시에 db 조회를 미루는 가짜(프록시) 엔티티 객체를 조회한다.
프록시는 실제 클래스를 상속 받기는 한다. 겉 모양만 같다. 그래서 사용하는 입장에서는 구분없이 사용하면 된다.
프록시는 실제 객체의 참조를 보관한다. 그래서 프록시 객체를 호출하면 실제 객체에서 가져오는 것이다.
클라이언트가 getReference 한 곳에 getName()을 신청을 하면 프록시는 영속성 컨텍스트에 초기화를 요청하고 db를 조회해서 실제 엔티티를 생성한다. 프록시는 이 실제 엔티티에서 target.getName() 을 호출하게 된다.
프록시 객체는 처음 한 번만 초기화 한다. 초기화를 한다고 실제 엔티티로 바뀌는 것은 아니다. 만약 영속성 컨텍스트에 찾는 엔티티가 이미 있으면 getReference()를 호출해도 실제 엔티티를 반환하게 된다.
emf.isLoaded(Object entity)시에 프록시 인스턴스의 초기화 여부를 확인할 수 있다.
entity.getClass() 하면 클래스를 확인할 수 있다.
hibernate.initialize(entity) 시에 강제 초기화 할 수 있다. 물론 getName()등으로 초기화할 수도 있다.
member를 호출할 때에 team을 같이 불러온다. member만 필요한데 team까지 조인되면 참 성능 맘에 안든다.
이것을 프록시를 통해 member만 불러오는 거다.
다음과 같이 말이다.
lazy를 추가안했으면 우측과 같이 안나올 것이다. 하지만 해줬더니 member 따로 team 따로가 나오고 프록시 클래스가 나오는 것을 알 수 있다.
child 와 parent가 서로 양방향 일 때에 다음과 같은 경우에 em.persist 를 원래라면 3번을 사용해줬어야 한다. 하지만 cascade=cascadeType.ALL를 통해 parent 만 함으로써 그 밑의 list 담긴 애들을 한 번에 영속성에 올려준다.
orphanRemoval 은 고아가 된 child를 자동삭제 해주는 기능이다.
다음과 같이하면 parent가 없어진 index 0 의 child는 자동 삭제된다.
다음과 같이 말이다.
지연로딩에 대해서인데, 만약 member 와 team을 같이 쓰고 싶다면 즉시로딩을 사용하면 된다. 이때에는 LAZY가 아닌 EAGER를 사용하면 된다. 실무에서는 지연로딩만 사용하자.
그리고 orphanRemoval 이나 cascade 사용 시에는 참조하는 곳이 1개인 특정 엔티티만 그것을 소유하고 있을 때에만 사용하도록 하자. 괜히 다른 곳에서 child를 참조하고 있다가 삭제되거나 하면 골치아파진다. 아 parent를 삭제하면 모든 child가 삭제된다.
실습 pjt
ManyToOne OneToOne 은 default가 즉시로딩이므로 지연로딩으로 다 바꿔주었고,
Order 와 Delivery, Order 와 OrderItem 을 영속성 전이 ALL 로 설정해주었다. (cascade)
실습 pjt end
'공부 기록들' 카테고리의 다른 글
앞으로 (0) | 2020.06.26 |
---|---|
2020.06.21-22 JPA(5) (0) | 2020.06.21 |
2020.06.17 JPA(3) (0) | 2020.06.17 |
2020.06.16 JPA(2) (0) | 2020.06.17 |
2020.06.15 JPA(1) (0) | 2020.06.15 |