Spring Boot - JPA 연관 관계

진경천·2024년 11월 18일

연관 관계 매핑이란?

객체의 참조와 테이블의 외래 키를 매핑하는 것을 의미

  • DB의 엔티티 간 관계를 정의하는 방법
  • 객체 모델과 RDB 간의 일관성을 유지
  • 객체 지향 프로그래밍과 데이터베이스 사이의 패러다임 불일치를 해결

방향성

  • 단방향
    두 엔티티 관계에서 한쪽 엔티티만 참조
  • 양방향
    두 엔티티 관계에서 양쪽 엔티티가 서로 참조.
    A -> B, B -> A

DB 테이블에서는 외래키 하나로 양 쪽 테이블 조인이 가능하다.
그러나 객체는 참조용 필드가 있는 객체만 다른 객체를 참조하는 것이 가능하다.
Table

연관 관계 주인

연관 관계가 양방향일 때, 관계에서 관리를 하는 주체

양방향 관계를 맺으며 객체가 서로 외래키를 가지게 되는데, 두 객체 중 하나는 외래키를 관리해야 하며 외래키를 관리하는 객체를 연관관계의 주인이라고 하낟.

연관관계 주인만이 외래 키를 관리(등록,수정,삭제)할 수 있으며, 주인이 아닌 객체는 읽기만 가능하다.

일반적으로 외래 키를 가진 객체각 연관 관계의 주인이 된다.

public class Post {
	@Id
    private Long id;
    @OneToMany(mappedBy = "post")	// 비주인
    private List<Comment> comments;
}

public class Comment {
	@Id
    private Long id;
    @ManyToOne
    @JoinColumn(name = "post_id")	// 주인
    private Post post;
}
Post post = new Post();
Comment comment = new Comment();
comment.setPost(post);	// 주인 측에서 등록
post.getComments().add(comment);

위와 같이 연관 관계의 주인은 @JoinColumn을 사용하고 주인인 아닌쪽은 @MappedBy를 사용한다.

정리

  • 연관 관계의 주인은 관계가 양방향일 때 성립
  • 연관 관계의 주인은 외래키가 있는 객체로 설정
  • 주인은 @JoinColumn을 사용해 외래키의 주인임을 명시
  • 주인이 아닌 쪽은 @mappedBy를 사용하여 주인을 지정

다중성

  • 일대일(1:1), 일대다(1:N), 다대일(N:1), 다대다(N:M) 존재
  • 연관관계를 어떤 엔티티를 중심으로 보는지에 따라 연관관계 표현은 달라짐

다대일(N:1)

데이터베이스 입장에서는 무조건 다(N)쪽에서 외래키를 관리

하나의 게시글에는 여러(N)개의 댓글을 작성할 수 있다.

다대일(N:1) 단방향

public class Post {
	@Id
    private Long id;
    private List<Comment> comments;
}

public class Comment {
	@Id
    private Long id;
    @ManyToOne
    @JoinColumn(name = "post_id")	// 주인
    private Post post;
}
  1. 댓글(comment)들은 하나의 게시글(post)에 달려있다. (댓글 N: 글 1)
  2. 댓글을 조회했을 때, 댓글이 속해 있는 게시글도 같이 조회된다.
  3. 게시글을 통해 댓글에 접근할 수 없다.

단방향이기 때문에 Comment에서 @ManyToOne만 추가함

다대일(N:1) 양방향

public class Post {
	@Id
    private Long id;
    @OneToMany(mappedBy = "post")	// 비주인
    private List<Comment> comments;
}

public class Comment {
	@Id
    private Long id;
    @ManyToOne
    @JoinColumn(name = "post_id")	// 주인
    private Post post;
}
  1. 댓글(comment)들은 하나의 게시글(post)에 달려있다. (댓글 N: 글 1)
  2. 댓글을 조회했을 때, 댓글이 속해 있는 게시글도 같이 조회된다.
  3. 게시글을 통해서도 댓글에 접근할 수 있다.

양방향 관계이기 때문에 Post에도 @OneToMany를 선언해 매핑 설정을 해주었다.
외래키가 있는 쪽이 주인이 되어 외래키를 관리하게 된다.

일대다(1:N)

N:1과 비슷할거 같지만 1에서 N쪽의 객체를 조작하는 것에서 차이가 있다.

일대다(1:N) 단방향

public class Post {
	@Id
    private Long id;
    @OneToMany
    @JoinColumn(name = "comment_id")
    private List<Comment> comments;
}

public class Comment {
	@Id
    private Long id;
    private Post post;
}

Post 엔티티는 Comment 테이블의 외래키를 저장할 방법이 없기 때문에 조인 및 업데이트 쿼리를 따로 날려야하는 문제가 있다.

그렇기 때문에 일대다(1:N) 단방향 연관 관계 매핑이 필요한 경우는 그냥 다대일(N:1) 양방향 연관 관계를 매핑해버리는게 추후에 유지보수에 훨씬 수월하다.

일대다(1:N) 양방향을 사용해야할 때는 다대일 양방향을 사용하는 것이 더 좋다.

일대일(1:1)

public class User {
	@Id
    private Long id;
    private String name;
    
    @OneToOne
    @JoinColumn
    privaet Profile profile;
}

public class Profile {
	@Id
    private Long id;
    private String bio;
    
    @Col

다대다(N:M)

publc class Student {
	@Id
    private Long id;
    @ManyToMany
    List<Course> courses;
}

public class Course {
	@Id
    private Long id;
}

실무에서 사용을 추천하지 않는다.

  • 중간 테이블이 숨겨져 있기 때문에 자기도 모르는 복잡한 조인의 쿼리(Query)가 발생하는 경우가 생길 수 있다.

  • 다대다로 자동생성된 중간테이블은 두 객체의 테이블의 외래 키만 저장되기 때문에 문제가 될 확률이 높다.

정리

관계 Annotation Entity 매핑 예시
다대일(N:1) @ManyToOne Comment(N) : Post(1) 여러 댓글에 하나의 글
일대다(1:N) @OneToMany Post(1) : Comment(N) 하나의 글에 여러 댓글
일대일(1:1) @OneToOne Country(1) : Capital(1) 나라와 수도, 사용자와 프로필 정보
다대다(N:M) @ManyToMany Order(N) : Item(M) 주문과 상품, 수업과 학생

즉시 로딩과 지연 로딩

JPA는 연관관계가 설정된 Entity의 정보를 바로 가져올지, 필요할 때 가져올지 정할 수 있는데, 이를 Fetch Type이라 한다.

  • 지연 로딩(Lazy): 필요한 시점에 정보를 가져온다. @OneToMany처럼 주인이 하나인 쪽의 Fetch Type의 default 값은 Lazy이다.
  • 즉시 로딩(EAGER): 조회할 때 연관된 모든 Entitiy의 정보를 즉시 가져온다. ManyToOne처럼 주인이 여러개인 쪽의 default 값은 EAGER이다.

왜 default 값이 위와 같이 정해져 있을까?
뒤쪽에 어노테이션 이름의 뒤쪽에 Many가 붙었다면 분명 데이터가 여러개일 가능성이 높다. 그렇기 때문에 효율적인 정보 조회를 위해 지연 로딩이 default이고, 반대로 이름 뒤쪽이 One일 경우 Entity 정보가 하나만 들어오기 때문에 즉시 정보를 가져와도 무리가 없어 즉시 로딩이 default이다.

엔티티 매핑 어노테이션

@Entity

  • 해당 클래스가 엔티티임을 명시

@Table

  • 데이터베이스에서 사용할 테이블 이름을
    명시

@Id

  • 엔티티의 고유 식별자 필드를 정의
  • 데이터베이스에서 기본키로 사용

@GeneratedValue

  • 기본 키의 값을 자동으로 생성하는 설정
  • 기본키 생성 전략 설정 가능
    • GeneratedType.IDENTITY는 데이터베이스에서 자동 증가 사용

@Column

  • 필드에 대한 제약 조건을 설정
  • name, nullable, unique 등 옵션 작용
    가능
profile
어중이떠중이

0개의 댓글