JPA 연관관계 매핑

Created:
Updated:

@OneToOne

엔티티 끼리 1:1로 연결할 경우 경우 사용한다.

예를들어 게시글(Post)과 작성자(Author) 엔티티가 있고 Post가 Author의 PK를 가진다고 할때 아래의 코드처럼 표현할 수 있다.

/**
 * Post
 */
@Entity
@Table(name = "post")
public class PostEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long postId;

    // (1)
    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "authorId")
    private AuthorEntity author;
}

(1): Post가 Author의 PK 필드를 가지도록 하는 코드이다. 실제로 Post 테이블에 authorId 컬럼이 생성된다.


/**
 * Author
 */
@Entity
@Table(name = "author")
public class AuthorEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long authorId;

    // (2)
    @OneToOne(mappedBy = "author", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    private PostEntity author;
}

(2): @OneToOne에 mappedBy가 붙으면 실제로 테이블에 컬럼이 생성되는건 아니고 데이터만 불러올 수 있다. 때문에 Author을 가져올 때 그에 매핑되는 Post가 있다면 Post 데이터도 함께 가져올 것이다. 그런데 이렇게 설계하게 되면 Author은 Post를 하나만 가질수 있게 된다;

@OneToMany / @ManyToOne

엔티티 끼리 1:N, N:1로 연결할 경우 사용한다.

예를들어 게시글(Post)과 댓글(Comment) 엔티티가 있고 Comment가 Post의 PK를 가진다고 할때 아래의 코드처럼 표현할 수 있다.

/**
 * Comment
 */
@Entity
@Table(name = "comment")
public class CommentEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long commentId;

    // (1)
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "postId")
    private PostEntity post;

}

(1): Comment가 Post의 PK 필드를 가지도록 하는 코드이다. 실제로 Comment 테이블에 postId 컬럼이 생성된다.


/**
 * Post
 */
@Entity
@Table(name = "post")
public class PostEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long postId;

    // (2)
    @OneToMany(mappedBy = "post", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    private List<CommentEntity> comments;

}

(2): @OneToMany mappedBy가 붙어야하고 실제로 Post 테이블에 컬럼이 생성되는건 아니다. Post를 가져올 때 그에 매핑되는 Comment 데이터들이 있다면 함께 가져온다.

@ManyToMany

엔티티 끼리 M:N 으로 연결할 경우 사용한다.

예를들어 주소(Address)와 작성자(Author) 엔티티가 있으며 Address가 Author를 여러개 가질 수 있고 Author도 Address를 여러개 가질수 있다고 할때 아래의 코드 처럼 표현할 수 있다.

@Entity
@Table(name = "address")
public class AddressEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long addressId;

    // (1)
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "address_author",
            joinColumns = @JoinColumn(name = "addressId"),
            inverseJoinColumns = @JoinColumn(name = "authorId"))
    private List<AuthorEntity> authors;
}

(1): 조인테이블이 별도로 생성된다. 위 코드 같은 경우는 address_author이라는 테이블이 생성되고 각 Address와 Author의 FK 컬럼이 생성된다. Address 데이터를 가져올시에 조인테이블에 매핑되는 Author 데이터들도 가져온다.


@Entity
@Table(name = "author")
public class AuthorEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long authorId;

    // (2)
    @ManyToMany(mappedBy = "authors")
    private List<AddressEntity> addresses;
}

(2): 이미 (1)과정에서 생성된 조인테이블을 통해 마찬가지로 Author 데이터를 가져올시에 매핑되는 Address 데이터들도 가져온다.

Option

Cascade

원래 엔티티 데이터의 생성,수정,삭제시 연관 엔티티 데이터도 연쇄적으로 처리해줄 것인지 여부를 설정할 수 있다. Cascade의 종류는 아래와 같다.
ALL, PERSIST, MERGE, REMOVE, REFRESH, DETACH
한번에 여러개의 Cascade를 설정해 줄 수도 있다.

PERSIST

원래 엔티티 데이터 등록,수정시 연관 엔티티의 데이터도 포함 되어있다면 연관 엔티티의 데이터까지 레코드가 생성된다.
만약 PERSIST 옵션 없이 연관 엔티티의 데이터를 추가하여 등록을 시도하면 아래와 같은 예외가 발생한다.
object references an unsaved transient instance - save the transient instance before flushing

REMOVE

원래 엔티티 데이터 삭제시 연결되있는 연관 엔티티 레코드까지 연쇄적으로 삭제한다. 만약 REMOVE 옵션 설정을 해놓지 않고 삭제하면 별다른 예외 발생없이 삭제가 무시된다.

orphanRemoval

옵션의 기본설정은 false로 되어있으며 true로 설정할 경우 메인 엔티티의 레코드가 삭제될시 연결되어있는 연관 엔티티의 레코드까지 자동으로 삭제된다.

만약 자동으로 삭제될때 연관엔티티 레코드가 다른곳에서도 사용중일 경우 에러가 발생한다.

Comments