[JPA] 다양한 연관관계 매핑 - 다대일, 일대다

Hocaron·2022년 3월 8일
1

Spring

목록 보기
18/44
post-custom-banner

다중성

JPA는 객체가 기준이지만, 다중성데이터베이스가 기준이다.
연관 관계는 대칭성을 갖는다.

다대일(N : 1)

게시판(Board)과 게시글(Post)이 있다.

요구사항

  • 하나의 게시판에는 여러 게시글을 작성할 수 있다.
  • 하나의 게시글은 하나의 게시판에만 작성할 수 있다.
  • 게시글과 게시판은 다대일 관계를 갖는다.
    게시글N : 게시판1, 즉, 외래 키를 게시글(N)이 관리하는 일반적인 형태이다.

다대일(N:1) 단방향

@Entity 
public class Post { 
	@Id @GeneratedValue 
    @Column(name = "POST_ID") 
    private Long id; 		
    
    @Column(name = "TITLE") 
    private String title; 
    
    @ManyToOne 
    @JoinColumn(name = "BOARD_ID") 
    private Board board; 
    //... getter, setter 
 } 
    
@Entity 
public class Board { 
	@Id @GeneratedValue 
    private Long id; 
    private String title; 
    //... getter, setter 
}

다대일 단방향에서는 다 쪽인 Post에서 @ManyToOne 만 추가해줬다.

다대일(N:1) 양방향

@Entity 
public class Post { 
	@Id @GeneratedValue 
    @Column(name = "POST_ID") 
    private Long id; 
    
    @Column(name = "TITLE") 
    private String title; 
    
    @ManyToOne 
    @JoinColumn(name = "BOARD_ID") 
    private Board board; 
    //... getter, setter 
} 

@Entity 
public class Board { 
	@Id @GeneratedValue 
    private Long id; 
    
    private String title; 
    
    @OneToMany(mappedBy = "board") 
    List<Post> posts = new ArrayList<>(); 
    //... getter, setter 
}
  • 다대일 관계에서 단방향 매핑을 진행하고, 양방향 매핑을 진행할 때
  • 반대쪽에서 일대다 단방향 매핑을 해주면 된다.(객체에서 컬렉션 추가해주면 된다.)
  • 여기서 중요한건, 반대에서 단방향 매핑을 한다고 해서 DB 테이블에 영향을 전혀 주지 않는다.
  • 다대일관계의 다 쪽에서 이미 연관관계의 주인이 되어서 외래키를 관리하고 있다.

다대일 양방향으로 만드려면 일(1) 쪽에 @OneToMany 를 추가하고 양방향 매핑을 사용했으니 연관관계의 주인이 아니고, 어디에 매핑 됐는지에 관한 정보 mappedBy를 꼭 넣어줘야 한다.
mappedBy로 지정할 때 값은 대상이 되는 변수명을 따라 지정하면 된다.

일대다(1:N)

데이터베이스 입장에서는 무조건 다(N)쪽에서 외래키를 관리한다.
하지만 일대다는 연관관계의 주인을 일(1)쪽에 둔것이다. 즉, 일(1)쪽 객체에서 다(N) 쪽 객체를 조작(생성,수정,삭제)하는 방법이다.

@Entity 
public class Post { 
	@Id @GeneratedValue 
    @Column(name = "POST_ID") 
    private Long id; 
    
    @Column(name = "TITLE") 
    private String title; 
    //... getter, setter 
} 

@Entity 
public class Board { 
	@Id @GeneratedValue 
    private Long id; 
    
    private String title; 
    
    @OneToMany 
    @JoinColumn(name = "POST_ID") //일대다 단방향을 @JoinColumn필수 
    List<Post> posts = new ArrayList<>(); 
    //... getter, setter 
}

일대다 단방향 매핑의 단점

//... 
Post post = new Post(); 
post.setTitle("가입인사"); 

entityManager.persist(post); // post 저장 

Board board = new Board(); 
board.setTitle("자유게시판"); 
board.getPosts().add(post); 
entityManager.persist(board); // board 저장 
//...

위와 같은 시나리오로 동작을 살펴보면, post를 저장할 때는 멀쩡하게 insert 쿼리가 나간다.
여기서 문제! board를 저장할 때는 Board를 insert하는 쿼리가 나간 후에 post를 update하는 쿼리가 나간다. board.getPosts().add(post); 부분 때문이다.

Board 엔티티는 Board 테이블에 매핑되기 때문에 Board 테이블에 직접 지정할 수 있으나, Post 테이블의 FK(BOARD_ID)를 저장할 방법이 없기 때문에 조인 및 업데이트 쿼리를 날려야 하는 문제가 있다.

🚨 치명적인 단점

  • 일만 수정한 것 같은데 다른 수정이 생겨 쿼리가 발생하는 것.
    • 업데이트 쿼리 때문에 성능상 이슈는 그렇게 크지는 않다.
      그렇기 때문에 TIP으로 일대다(1:N) 단방향 연관 관계 매핑이 필요한 경우는 그냥 다대일(N:1) 양방향 연관 관계를 매핑해버리는게 추후에 유지보수에 훨씬 수월하기 때문에 이 방식을 채택하는 것을 추천한다.

그런데 실무에서 사용을 금지하지 않는 이유는 되도록 피하는 게 좋지만, JPA 값 타입을 사용하는 것을 대신하여 사용할 때는 또 유용하다.
→ 일대다(1:N) 양방향 (실무 사용 금지 ❌)
@JoinColumn(updatable = false, insertable = false)로 읽기 전용으로 추가할 수 있지만...

결과적으로 일대다(1:N) 단방향, 양방향은 쓰지 말고 차라리 다대일(N:1) 양방향으로 쓰는 것이 맞다.

References

profile
기록을 통한 성장을
post-custom-banner

0개의 댓글