11월 21일 목요일
AM 알고리즘 풀이, 과제 수행
PM 과제 수행
Spring JPA 연관관계
- 1:1 | @OneToOne
- 1:N | @OneToMany
- N:1 | @ManyToOne
- N:M | @ManyToMany
N:M 연관관계는 RDM에서 일반적인 방법으로 표현할 수 없어서 중간테이블이 생기게 됩니다.
따로 중간 테이블을 만들어서 1:N, N:1 관계로 분해하지 않으면 관리가 힘들어지기 때문에 사용을 권장하지 않습니다.
1:1 연관관계도 사용 시 객체 지향적으로 개발할 수 있다는 점 등 장점도 있지만, 단점도 존재하기에 주의해서 사용해야 됩니다.
@Entity
@Getter @Setter
public class User {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany
private List<Post> posts;
@OneToMany
private List<Comment> comments;
}
@Entity
@Getter @Setter
public class Post {
@Id @GeneratedValue
private Long id;
private String title;
@ManyToOne
private User user;
@OneToMany
private List<Comment> comments;
}
@Entity
@Getter @Setter
public class Comment {
@Id @GeneratedValue
private Long id;
private String text;
@ManyToOne
private Post post;
@ManyToOne
private User user;
}
서로 각 방향의 반대 상황이라고 생각하면 된다.
연관관계에는 주인이라는 개념이 있다.
주인은 연관관계의 관리 주체이며, 주인은 이 연관관계를 관리할 책임이 있다.
언뜻 생각하기에 부모 엔티티가 주인 엔티티가 아닌가 생각할 수 있지만 주인 엔티티와 부모 엔티티는 분명히 다르다.
가장 많이 쓰이는 연관관계입니다. 엔티티의 관계를 표현하고 FK 관리에 있어서 가장 자연스럽기 때문입니다.
@JoinColumn 어노테이션과 함께 쓰이며, 이때 @JoinColumn은 엔티티 테이블에 FK 칼럼을 정의해줍니다.
/* Post.java */
@ManyToOne
@JoinColumn
private User user
상대 엔티티를 참조할 수 있는 매핑이 부모 엔티티 쪽에 존재하지만, FK는 자식 엔티티 테이블에 존재하는 연관관계입니다.
@JoinColumn없이 사용할 경우, Hibernate에서 자체적으로 중간 테이블(link table)을 생성해서 연관관계를 관리하게 됩니다.
create table "user" (
id bigint generated by default as identity,
name varchar(255),
primary key (id)
)
create table "user_posts" (
"user_id" bigint not null,
posts_id bigint not null
)
create table post (
id bigint generated by default as identity,
title varchar(255),
primary key (id)
)
post 테이블에 FK 칼럼이 없기 때문에 user와 post 테이블을 연결할 수 있도록 중간 테이블이 생성되었습니다.
이렇게 되면 FK칼럼을 만드는 것보다 비효율적이며, 무엇보다 자식 엔티티를 제거할 때 심각한 성능 문제로 이어질 수 있습니다. 밑에서 더 자세히 다루겠습니다.
따라서, 이를 방지하기 위해 @JoinColumn을 같이 사용할 수 있습니다.
/* User.java */
@OneToMany
@JoinColumn(name = "user_id")
private List<Post> posts;
단방향 @ManyToOne과 더불어 가장 많이 쓰이는 연관관계입니다.
DB 관점에서는 단방향 @ManyToOne과 차이점이 없으며, 어플리케이션에서 상대 엔티티쪽에서 참조할 수 있는 변수가 생기는 장점이 있습니다.
/* User.java */
@OneToMany(mappedBy = "user")
private List<Post> posts;
/* Post.java */
@ManyToOne
@JoinColumn
private User user
@OneToMany쪽에 mappedBy를 설정해서 상대 엔티티에서 어떻게 매핑이 되어 있는 지를 설명하고 @ManyToOne쪽에서 @JoinColumn을 통해 같은 테이블 내에서 FK 칼럼을 정의합니다.
여기서 @OneToMany에 mappedBy를 설정하지 않으면 @OneToMany가 단방향처럼 취급되어 중간 테이블이 생성되기 때문에 주의가 필요합니다.
해당 객체를 DB에서 조회할 때, 연관관계에 있는 엔티티의 정보를 언제 같이 끌어올 지를 결정한다.
연관관계에 있는 엔티티에 접근할 때, DB에 쿼리를 날려 엔티티를 조회하게 됩니다.
접근하지 않는 경우, 쿼리가 발생하지 않습니다.
상대 엔티티의 조회 여부와 상관없이, 쿼리가 발생하게 됩니다.
@OneToMany의 기본값은 Lazy Fetch이며, @ManyToOne의 기본값은 Eager Fetch입니다.
Eager Fetch, Lazy Fetch 상관 없이 단건 조회가 아닌 컬렉션 조회에서 N+1 문제가 발생할 수 있습니다.
Eager Fetch는 조회 여부와 상관없이 쿼리가 발생하기 때문에, 더 잘 보이는 차이가 있을 뿐입니다.
CascadeType으로 6가지를 줄 수 있습니다.
PERSIST, MERGE, REMOVE, REFRESH, DETACH와 모든 옵션을 줄 수 있는 ALL입니다.
영속성 전파를 설정하게 되면, 객체에 해당 작업이 이루어질 때, 자식엔티티에도 작업이 전파됩니다.
예를 들어, 유저의 posts에 cascadeType으로 PERSIST가 걸려있으면, 유저 객체만 저장해도 글 객체도 저장됩니다.
optional 옵션은 @ManyToOne에만 존재하는 옵션입니다.
해당 옵션은 FK 칼럼에 Null 여부를 설정합니다.
기본값은 true이며, false인 경우, FK에 Null을 허용하지 않습니다.
여전히 배울게 많다고 느낀다