연관 관계
객체의 연관 관계와 테이블의 연관 관계의 차이는 먼저 테이블은 FK(외래키)를 참조하여 연관된 테이블을 찾고, 객체는 또다른 객체를 참조하여 연관된 객체를 찾는다.
테이블은 조인을 통해 양방향 연관 관계 설정이 가능하지만 객체에는 양방향 연관 관계 설정이 없으므로 다른 두 객체에 각각 단방향 연관 관계를 설정해야 한다.
만약 게시물(Board)객체와 댓글(Reply)객체를 생성했을 때, 내가 특정 게시물 객체에서 댓글을 사용하고 싶다면 어떻게 해야 할까?
@ManyToOne 단방향
@ManyToOne이란 다대일 관계를 말하며, 위의 두 객체로 예시를 들자면 하나의 게시물 기준으로 여러 댓글을 작성을 하는 관계를 말할 수 있을 것이다.
다:일 관계에서 일반적으로 다수에 1의 PK(기본키) 가 FK(외래키)로 생성되서, JPA에서는 연관관계의 주인이FK(외래키)를 관리하게 된다.
따라서 FK(외래키)가 존재하는 다수 쪽이 연관관계의 주인이 된다.
그렇다면 주인이 되는 Reply 객체에는 Board 타입의 객체 참조를 board라는 변수를 이용하여 참조해야 하기에 두 객체를 연결하기 위해서 단방향 @ManyToOne을 선언한다.
Board 엔티티
1
2
3
4
5
6
7
8
9
10
11
12
13
| @Entity
public class Board{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long bno;
private String title;
private String content;
private String user;
}
|
Reply 엔티티
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| @Entity
public class Reply{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY )
private Long rno;
@ManyToOne(fetch = FetchType.LAZY)
private Board board;
private String replyText;
private String replyer;
}
|
@ManyToOne 양방향
양방향 방식은 말그대로 Board에서 Reply을 참조하고 반대의 경우도 Reply에서도 Board를 참조하는 방식이다.
단방향과 똑같이 연관관계 주인이 FK 관리하며 반대쪽은 어차피 읽기만 가능하기 때문에 Board에서 List를 추가하기만 하면 된다.
이때 @mappedBy로 연관관계의 주인을 읽을 것이라는 것 명시가 중요하다.
이것은 단순히 ReadOnly 읽기전용 필드가 되기에 FK 값의 변동은 일어나지 않는다. 연관관계의 주인은 Board_bno FK를 관리하는 Reply 엔터티로 지정했기 때문에 Board에서는 FK를 관리 할 수 없다.
Board 엔티티
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| @Entity
public class Board{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "board_bno")
private Long bno;
private String title;
private String content;
private String user;
@OneToMany(mappedBy = "board")
private List<Reply> replys = new ArrayList<>();
}
|
Reply 엔티티
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| @Entity
public class Reply{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY )
private Long rno;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "board_bno")
private Board board;
private String replyText;
private String replyer;
}
|
@OneToMany 단방향
@OneToMany 는 기본적으로 상위 엔티티과 여러개의 하위 엔티티들의 구조로 이루어진다.
@ManyToOne와 다른 점은 @ManyToOne은 다른 엔티티 객체의 참조로 FK(외래키)를 가지는 쪽에서 하는 방식이고, 반대로 @OneToMany은 PK(기본키)를 가진 쪽에서 사용하는 점이다.
Board객체는 Reply 객체를 알고싶은데 Reply는 Board을 알고싶지 않는 구조 인것이다.
Reply 엔티티
1
2
3
4
5
6
7
8
9
10
11
12
13
| @Entity
public class Reply{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY )
private Long rno;
private Board board;
private String replyText;
private String replyer;
}
|
Board 엔티티
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| @Entity
public class Board{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long bno;
private String title;
private String content;
private String user;
@OneToMany
@JoinColumn(name = "reply_title")
private List<Reply> replyList = new ArrayList<>();
}
|
보통 다대일 단반향 방식은 엔티티가 관리하는 외래키가 다른 테이블에 있기도 하고 연관관계 관리를 위해 추가로 UPDATE SQL 실행을 해야 하기 때문에 운영 및 관리하기 힘들기 때문에 다대일 양방향 방식을 사용하는 것을 지향하자.
@OneToMany 양방향
@OneToMany 양방향 방식은 JPA에서 공식적으로 존재하지 않는다.
Reply 에서 Team JoinColumn 에 insertable=false, updatable=false 를 줘서 읽기전용으로 만든다.
Reply 엔티티
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| @Entity
public class Reply{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY )
private Long rno;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "board_bno", ,insertable=false, updatable=false)
private Board board;
private String replyText;
private String replyer;
}
|
Board 엔티티
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| @Entity
public class Board{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "board_bno")
private Long bno;
private String title;
private String content;
private String user;
@OneToMany(mappedBy = "board")
@JoinColumn(name = "board_bno")
private List<Reply> replys = new ArrayList<>();
}
|
이런식으로 구성하면 Board, Reply모두 @JoinColumn이 붙어서 둘다 연관관계의 주인이된다. 그래서 JoinColumn의 옵션을 사용해서 mapping은 되어있고 값은 다 쓰는데 insertable, updatable을 막아 read 전용으로 만든다.
관리는 Board으로하고 Reply는 읽기만한다. 무언가 기이한 구조가 되기 때문에 위의 @OneToMany의 단점을 나열했을 때도 작성했지만 @ManyToOne 양방향를 사용하도록 하자