JPA는 객체가 기준이지만, 다중성은 데이터베이스가 기준이다.
연관 관계는 대칭성을 갖는다.
게시판(Board)과 게시글(Post)이 있다.
@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
만 추가해줬다.
@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
}
다대일 양방향으로 만드려면 일(1) 쪽에 @OneToMany
를 추가하고 양방향 매핑을 사용했으니 연관관계의 주인이 아니고, 어디에 매핑 됐는지에 관한 정보 mappedBy
를 꼭 넣어줘야 한다.
mappedBy
로 지정할 때 값은 대상이 되는 변수명을 따라 지정하면 된다.
데이터베이스 입장에서는 무조건 다(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)를 저장할 방법이 없기 때문에 조인 및 업데이트 쿼리를 날려야 하는 문제가 있다.
🚨 치명적인 단점
그런데 실무에서 사용을 금지하지 않는 이유는 되도록 피하는 게 좋지만, JPA 값 타입을 사용하는 것을 대신하여 사용할 때는 또 유용하다.
→ 일대다(1:N) 양방향 (실무 사용 금지 ❌)
@JoinColumn(updatable = false, insertable = false)
로 읽기 전용으로 추가할 수 있지만...
결과적으로 일대다(1:N) 단방향, 양방향은 쓰지 말고 차라리 다대일(N:1) 양방향으로 쓰는 것이 맞다.