JPA(2) - 인프런

JPA(2)-인프런

[1] 상속관계 매핑(1, 조인전략, 2. 단일테이블, 3. 구현클래스)

상속관계 매핑 : 객체의 상속과 구조와 DB의 슈퍼타입 서브타입 관계를 매핑

슈퍼타입 서브타입 논리 모델을 실제 물리 모델로 구현하는 방법

image

1) 조인 전략 - 각각 테이블로 변환

image

@Entity
@Inheritance(strategy=InheritanceType.JOINED)
@Discriminator //DTYPE컬럼을 만들고 자식클래스 명이 들어간다.(ALBUM, MOVIE ..)
public class Item {
	...
}
@Entity
public class Album extends Item { ... }

장점

  1. 테이블 정규화
  2. 외래키 참조 무결성 제약조건 활용 가능(주문 테이블에서 ITEM 테이블만 보면 된다.)
  3. 저장공간 효율화

단점

  1. 조회 시 조인을 많이 사용하여 성능 저하
  2. 데이터 저장 시 INSERT SQL 2번 호출
2) 단일 테이블 전략 - 통합 테이블로 변환(한 테이블에 다 들어간다)

image

@Inheritance(strategy= InheritanceType.SINGLE_TABLE)
// 어느 자식클래스인지 구분하기 위해 DTYPE은 필수로 생성된다.

장점

  1. 조회 쿼리가 단순하고 조인이 없어 성능이 좋다

단점

  1. 자식 엔티티가 매핑한 컬럼은 모두 null 허용
  2. 단일 테이블에 저장하므로 테이블이 커질 수 있다.
3) 구현 클래스마다 테이블 전략 - 서브타입 테이블로 변환

image

@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
// Item Class는 Abstract 추상 테이블로 만들어준다.
// 클래스별로 분리되므로 @Disciminator는 무의미하다.

쓰지 말아야할 전략!!!
테이블을 묶어낼 수가 없다. 조회하려면 모두 UNION ALL해야한다.
컬럼 추가 시에도 모두 추가해줘야한다.


[2] @MappedSuperclass

공통 매핑정보가 필요할때 사용(id, name, regDt, regUsrId, updDt, updUsrId)

DB는 완전 다른데 속성값을 공통으로 사용한다.(추상 클래스 사용)

image

@MappedSuperClass
class abstract BaseEntity{
	LocalDateTime regDt;
	String regUsrId;
}

[3] 프록시와 연관관계 관리

1) 프록시 기초

image

껍데기에 아이디 값만 갖고 있다.

2) 프록시 특징

image

3) 프록시 객체의 초기화
Member member = em.getReference(Member.class, "id1");
member.getName();

image

(2. 초기화요청)은 Member target에 값이 없을 때 초기화를 요청한다.

4) 프록시 특징
5) 프록시 확인

[4] 즉시 로딩과 지연 로딩

1) Member를 조회할 때 Team도 함께 조회해야 할까?

단순히 member정보만 사용할 경우 Team까지 조회하면 낭비가 된다.
그러므로 지연로딩 LAZY를 사용해서 프록시로 조회할 수 있다.(즉시 로딩은 EAGER)

@Entity
public class Member {
    @Id
    @GeneratedValue
    private Long id;
    
    @Column(name = "USERNAME")
    private String name;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "TEAM_ID")
    private Team team;
}

image

Team team = member.getTeam();
team.getName(); // 실제 team을 사용하는 시점에 초기화(DB조회)
2) 프록시와 즉시로딩 주의

[5] 영속성 전이 CASCADE

특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함꼐 영속 상태로 만들고 싶을 때 사용한다.

예) 부모 엔티티를 저장할 때 자식 엔티티도 함께 저장(연관 아이들을 함께 persist)

@OneToMany(mappedBy="parent", cascade=CascadeType.PERSIST)

image

영속성 전이는 연관관계를 매핑하는 것과 아무 관련이 없다.

엔티티를 영속화 할 때 연관된 엔티티도 함께 영속화하는 편리함을 제공할 뿐!


[6] 고아 객체

고아객체 제거 : 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제한다.

orphanRemoval = true;

부모를 제거하면 자식은 고아가 된다.

고아 객체 제거기능을 활성화하면, 부모를 제거할 때 자식도 함께 제거된다.

CascadeType.REMOVE처럼 동작한다.


[7] 영속성 전이 + 고아객체, 생명주기

CascadeType.ALL + orpahnRemoval = true

스스로 생명주기를 관리하는 엔티티는 em.persist()로 영속화, em.remove로 제거

두 옵션을 모두 활성화하면 부모 엔티티를 통해서 자식의 생명 주기를 관리할 수 있다.


[8] 값 타입

1) JPA의 데이터 타입 분류
2) 기본값 타입
3) 임베디드 타입(복합 값 타입)

image


[9] 값 타입과 불변 객체

1. 값 타입 공유 참조

image

따라서 값타입의 실제 인스턴스 값을 복사해서 사용하여야한다.

setCity(new Address("NewCity"))

image

객체 타입의 한계

  • 임베디드 타입처럼 직접 정의한 값 타입은 객체 타입이다.
  • 기본 타입에 값을 대입하면 값을 복사하지만 객체 타입은 참조 값을 직접 대입하게 된다.
  • 객체의 공유 참조는 피할 수 없다.
Address a = new Address(Old);
Address b = a; //객체 타입은 참조를 전달
b. setCity(New)
2) 불변 객체
3) 값 타입의 비교

[10] 값 타입 컬렉션

image

1) 값 타입 컬렉션
@ElementCollection
@CollectionTable(name = "FAVORITE_FOOD"
                ,joinColumns=@JoinColumn(name="MEMBER_ID"))
private Set<String> favoriteFoods = new HashSet<>();

// 수정
findMember.getFavoriteFoods().remove("치킨");
findMember.getFavoriteFoods().add("한식");
findMember.getAddressHistory().remove(new Address("old", "street", "10000"));
findMember.getAddressHistory().add(new Address("new", "street", "10000"));
// 값 타입 컬렉션에 변경사항이 발생하면 주인 엔티티와 연관된 모든 데이터를 삭제하고
// 값 타입 컬렉션에 있는 현재값을 모두 다시 저장한다.
// 따라서 값 타입 컬렉션 대신에 일대다 관계를 고려하는 것이 바람직하다.
// 일대다 관계에서 영속성전이(cascade) + 고아객체 제거를 사용해서 값 타입 컬렉션처럼 사용가능하다.
2) 엔티티 타입의 특징
3) 값 타입의 특징

값타입은 정말 값타입이라 판단될떄만 사용한다!

식별자가 필요하고 지속해서 값을 추적, 변경해야한다면 그것은 값타입이 아닌 엔티티이다.

Comments

comments powered by Disqus