(W8D3) 연관관계 매핑, 프록시와 연관관계

단일 엔티티 매핑

JPA는 Entity 객체를 생성할 때, 기본 생성자를 사용한다.

  • @Table : 매핑할 테이블 이름
  • @Column

    속성 기능 기본값
    name 필드와 매핑할 테이블의 컬럼이름 객체의 필드 이름
    insertable Entity 저장 시 필드도 같이 저장한다. (false : 읽기 전용) true
    updatable Entity 수정 시 필드도 같이 수정한다. (false : 읽기 전용) true
    nullable null 값 허용 여부를 설정한다. (false : not null 제약조건이 추가됨) true
    unique 해당 컬럼의 유니크 제약조건을 추가한다.
    length 문자 길이 제약조건으로 사용한다. (String 타입에만 사용) 255

기본키 매핑 전략

  • @Id
  • @GeneratedValue(strategy = GenerationType.전략이름)

    • SEQUENCE
    • 데이터베이스 시퀀스에서 식별자 값을 획득한 후 영속화(Oracle, H2)
    • TABLE
    • 데이터베이스 시퀀스 생성용 테이블에서 식별자 값을 획득한 후 영속화
    • 키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내내는 전략
    • IDENTITY : 데이터베이스 Entity를 저장해서 식별자 값을 획득한 후 영속화(MySQL-AUTO_INCREMENT)
    • 엔티티가 영속화 되려면 식별자 값이 반드시 필요하기 때문에, entityManager.persist() 시점에 INSERT 쿼리가 수행된다.
    • entityManager.persist()가 호출되자마자 INSERT SQL을 통해 DB에서 식별자를 조회하여 영속성 컨텍스트의 1차 캐시에 값을 넣는다. (SELECT 쿼리를 다시 날리지 않아도 된다.)
    • 다른 전략에서는 이미 id 값을 알고 있기 때문에 commit 하는 시점에 INSERT 쿼리를 날린다.
    • AUTO
    • 데이터베이스 방언(dialect)에 따라서 자동으로 전략을 선택

연관관계 매핑

객체 연관관계 vs 테이블 연관관계

  • 테이블은 외래키로 연관 관계를 맺는다.
  • 객체는 참조(주소)로 연관 관계를 맺는다.

방향

  • 단방향 : 둘 중 한 쪽만 참조하는 것을 단방향 관계라고 한다.
  • 양방향 : 양 쪽 모두 서로 참조하는 것을 양방향 관계라고 한다.
  • 테이블에서의 관계는 항상 양방향이다. 테이블은 외래키를 이용해서 양방향으로 JOIN이 가능하다.

다중성 (다대일, 일대다, 다대다)

연관관계 주인(mappedBy)

  • 객체를 양방향 연관관계로 만들면, 연관관계의 주인을 정해야 한다.
  • 외래키를 관리할 객체를 지정한다. (INSERT, UPDATE, DELETE)
  • 연관관계 주인만이, 외래키를 등록, 수정, 삭제할 수 있다. (주인이 아닌 쪽은 읽기만 가능하다.)
  • 테이블 중 FK가 있는 쪽이 연관 관계 주인이 된다.

고급 매핑

JPA는 RDB의 테이블과 매핑된 객체(Entity)를 객체답게 사용할 수 있도록 여러가지 고급 매핑 전략을 제공해준다.

상속 관계 매핑

  • 조인 테이블 전략

    @Inheritance(strategy = Interitance.JOINED)
  • 싱글 테이블 전략

    @Entity
    @Table(name = "item")
    @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
    @DiscriminatorColumn(name = "DTYPE")
    public abstract class item {
    }
    
    @Entity
    @DiscriminatorValue("FOOD")
    public class Food extends Item {
    }

현업에서는 싱글 테이블 전략을 많이 사용한다. (조인 테이블 전략은 객체지향적이지 않다고 한다.)

@MappedSuperclass

  • 객체의 입장에서 공통 매핑 정보가 필요할 때 사용한다.
  • 단순히 매핑 정보를 상속할 목적으로만 사용된다.
  • @MappedSuperclass가 선언되어 있는 클래스는 엔티티가 아니다.
  • 테이블과는 관계가 없고 단순히 Entity가 공통으로 사용하는 매핑 정보를 모아주는 역할을 할 뿐이다.
@MappedSuperclass
public class BaseEntity {
    @Column(name = "created_by")
    private String createdBy;
    @Column(name = "created_at", columnDefinition = "TIMESTAMP")
    private LocalDateTime cratedAt;
}

식별자 클래스

JPA에서 식별자를 둘 이상 사용하려면 별도의 식별자 클래스를 만들어야 한다.

JPA는 영속성 컨텍스트에 Entity를 보관할때, eqauls & hashCode를 이용해서 동등성 비교를 한다

@IdClass

  • Serializable 인터페이스를 구현해야 한다.
  • eqauls, hashCode를 구현해야 한다.
  • 기본 생성자가 있어야 한다.
  • 식별자 클래스는 public 이어야 한다.
@Entity
@IdClass(ParentId.class)
public class Parent {
    @Id
    private String id1;
    @Id
    private String id2;
}

@EqualsAndHashCode
public class ParentId implements Serializable {
    private String id1;
    private String id2;
}

@EmbeddedId -> 객체지향스럽다!

  • Serializable 인터페이스를 구현해야 한다.
  • eqauls, hashCode를 구현해야 한다.
  • 기본 생성자가 있어야 한다.
  • 식별자 클래스는 public 이어야 한다.
  • @Embeddable 어노테이션이 있어야 한다.
@Entity
public class Parent2 {
    @EmbeddedId
    private ParentId2 id;
}

@EqualsAndHashCode
@Embeddable
public class ParentId2 implements Serializable {
    private String id1;
    private String id2;
}

프록시와 연관관계

Entity는 객체가 데이터베이스(RDB)와 매핑되어 있어서 자유롭개 객체를 탐색하는데 제한이 있다.

JPA는 프록시라는 기술을 사용하여 연관된 객체를 처음부터 데이터베이스에서 조회하지 않고, 실제 사용하는 시점에 조회할 수 있다.

프록시의 특징

  • 프록시 객체는 처음 사용할 때 한 번만 초기화 된다.
  • 프록시 객체가 초기화되면, 프록시 객체를 통해서 실제 Entity에 접근할 수 있다.
  • 초기화는 영속성 컨텍스트의 도움을 받아야 가능하다. 따라서 준영속 상태의 프록시를 초기화하면 LazyInitializationException 예외가 발생한다.

지연로딩 & 즉시로딩

프록시 객체는 주로 연관된 엔티티를 지연 로딩할 때 사용한다.

지연로딩

  • 엔티티를 조회할때, 연관된 엔티티를 함께 조회한다.
  • @ManayToOne(fetch = FatchType.EAGER)

즉시로딩

  • 연관된 엔티티를 실제 사용할 때 조회한다.
  • @ManayToOne(fetch = FatchType.EAGER)
  • JPA 구현체는 즉시 로딩을 최적화하기 위해 가능하면 조인 쿼리를 사용한다.

영속성 전이(CASCADE)

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

영속성 전이를 사용하면 부모 엔티티를 저장할 때 자식 엔티티도 함께 저장할 수 있다.

고아 객체

  • JPA는 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제하는 기능을 제공한다. -> 고아 객체 제거
  • 부모 엔티티의 컬렉션에서 자식 엔티티의 참조만 제거하면 자식 엔티티가 자동으로 삭제된다.
  • 고아 객체 삭제 : 참조가 제거된 엔티티는 다른 곳에서 참조하지 않는 고아 객체로 보고 삭제하는 기능이다. -> orphanRemoval은 @OneToOne, @OneToMany 에서만 사용할 수 있다.

Written by@Myunghwan
Nothing changes if nothing changes

GitHub