현재 서비스되고 있는 웹 어플리케이션중에 테이블을 단일 테이블로 사용하는 서비스는 별로 없을 것이다. 그렇기에 보통은 테이블끼리 연관을 지어서 사용하여 효율성을 높이는 방식을 많이 사용한다. 그래서 MyBatis같은 Mapper는 사용자가 직접 연관관계를 DBeaver같은 도구로 관계를 설정할 수 있다.
하지만 JPA를 사용한 경우에는 Entity객체끼리 연관을 지정해두면 자동으로 Key가 DB에 생성이 된다.
이 글에서는 OneToMany와 ManyToOne에 대해서만 말하겠다.
데이터베이스의 1:N / N:1 두 개의 의미를 알고있으면 이해하기 쉬울 것이다.
OneToMany : @JoinColumn을 사용하는 Entity의 반대 Entity가 연관관계의 주인, 즉 FK를 가진다. (테이블상에서는 반대 테이블에 컬럼과 FK가 생긴다.)
ManyToOne : @JoinColumn을 사용하는 Entity가 연관관계의 주인, 즉 FK를 가진다. (테이블상에서는 대상 테이블에 컬럼과 FK가 생긴다.)
호텔에 손님(member)이 왔고 방(room)을 등록한다, 방을 등록했고, 호텔에 막 도착한 고객은 아침식사를 안해서 룸서비스(roomservice)를 주문했고 커피와 토스트를 주문했다.
이시나리오를 테이블로 구성해보면 간략하게 이렇게 나올 수 있겠다.
member 테이블
투숙객의 고유번호(seq), 지내는방의 일련번호(room_seq), 투숙객의 성함(name), 나이(age)가 있어야 한다.
room 테이블
지내는방의 이름(name), 방의 고유의 일련번호(seq)가 있어여 한다.
roomservice 테이블
서비스 주문번호(seq), 드링크(drink), 음식(food), 어느방에서 시켰는지에 대한 방의 일련번호(room_seq) 그리고 이건 선택적인데 누가 시켰는지에 대한 고객번호(member_seq)가 필요하다.
Member Entity
@Getter
@Entity
@ToString
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "SEQ")
@Comment(value = "일련번호")
private int seq;
@Column(name = "NAME", length = 50)
@Comment(value = "이름")
private String name;
@Column(name = "AGE")
@Comment(value = "나이")
private int age;
public Member(String name, int age){
this.name = name;
this.age = age;
}
}
Room Entity
@Getter
@Entity
@ToString
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Room {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "seq")
@Comment(value = "일련번호")
private int seq;
@Column(name = "room_name", length = 50)
@Comment(value = "이름")
private String roomname;
@Comment(value = "멤버번호")
@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name = "room_seq")
private List<Member> member;
public Room(String roomname, List<Member> member){
this.roomname = roomname;
this.member = member;
}
}
Roomsercie Entity
@Getter
@Entity
@ToString
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Roomservice {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "SEQ")
@Comment(value = "일련번호")
private int seq;
@Column(name = "food", length = 50)
@Comment(value = "주문음식")
private String food;
@Column(name = "drink", length = 50)
@Comment(value = "주문드링크")
private String drink;
@Comment(value = "숙소번호")
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "room_seq")
private Room room;
@Comment(value = "투숙객번호")
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_seq")
private Member member;
public Roomservice(String food, String drink, Room room, Member member){
this.food = food;
this.drink = drink;
this.room = room;
this.member = member;
}
일단 이렇게 세 가지 타입의 Entity를 만들었고 공통적으로 생성자를 넣었다. 그리고 실행을 하면 테이블이 생성이 되고 외래키가 만들어진다.
테이블 생성
외래키 생성
DBeaver로 보는 연관관계
public void insertRoom() {
// Room에 등록할 용도로 Member Type의 ArrayList생성
List<Member> memberList = new ArrayList<>();
for(int i = 0; i < 3; i++){ // 고객등록
String people = "people_"+i;
Member member = new Member(people,20); // Member Entity객체 생성
memberList.add(member); // 리스트에 Member객체 추가
em.persist(member); // Member 테이블에 insert 쿼리추가
}
// Room 테이블에 normalRoom 등록쿼리 및 member 테이블에 등록된 사용자의 Room_seq 수정 쿼리추가
Room room = new Room("normalRoom",memberList);
em.persist(room);
// 등록된 Room, 등록된 Member, 음식, 드링크 추가하는 쿼리추가
Roomservice rooomservice = new Roomservice("toast", "coffee", room, memberList.get(0));
em.persist(rooomservice);
}
시나리오작성 내용대로 프로그램을 작성해보았다.
이런식으로 테이블간의 관계를 간략하게나마 설명하고 예시를 작성해보았는데 확실히 테이블 관계 표현을 나타내보고 싶으면 가상의 시나라오를 작성하고 테이블을 작성해보는게 나의 기준에서는 최고의 공부방법인거 같았다. 이 방법은 다른 사람들에게도 알려주고 싶다.
https://dev-coco.tistory.com/106
https://ksh-coding.tistory.com/105
https://soojong.tistory.com/entry/JPA-ManyToOne-OneToMany-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0
(항상 감사합니다.)