연관 관계 고민
고객 테이블
한명의 고객은 음식을 여러개를 주문할 수 있습니다.
주문한 음식의 정보를 파악하기 위해 food_id 컬럼을 추가합니다.
고객 테이블에 주문 정보를 넣습니다.
| id | name | food_id |
|---|---|---|
| 1 | Robbie | 1 |
| 2 | Robbert | 1 |
| 3 | Robbie | 2 |
Robbie는 후라이드 치킨, 양념 치킨을 주문했습니다.
불필요하게 고객의 이름이 중복되는 문제가 발생합니다.
음식 테이블
하나의 음식은 여러명의 고객에게 주문될 수 있습니다.
주문한 고객의 정보를 파악하기 위해 user_id 컬럼을 추가합니다.
음식 테이블에 주문 정보를 넣습니다.
| id | name | price | user_id |
|---|---|---|---|
| 1 | 후라이드 치킨 | 15000 | 1 |
| 2 | 후라이드 치킨 | 15000 | 2 |
| 3 | 양념 치킨 | 20000 | 1 |
후라이드 치킨을 여러사람이 주문하게되었을 때!
불필요하게 음식의 이름이 중복되는 문제가 발생합니다.
| ID | name | price | user_id |
|---|---|---|---|
| 1 | 후라이드치킨 | 15000 | 1,2 |
그렇다면 위 표처럼 ‘데이터베이스에 저장될 때 user_id 컬럼에 1,2 로 저장하면 되지 않을까?’라고 생각할 수도 있지만!
user_id 값을 계속해서 추가할 때 뿐만 아니라 고객의 정보도 함께 조회가 필요할 때 많은 문제가 발생할 수 있기 때문에 현실적으로 불가능합니다.
주문 테이블
주문에 대한 정보를 기록할 orders 테이블을 추가합니다.
주문 테이블을 사용하여 테이블들의 연관 관계를 해결합니다.
고객
| id | name |
|---|---|
| 1 | Robbie |
| 2 | Robbert |
음식
| id | name | price |
|---|---|---|
| 1 | 후라이드 치킨 | 15000 |
| 2 | 양념 치킨 | 20000 |
| 3 | 고구마 피자 | 30000 |
| 4 | 아보카도 피자 | 50000 |
주문
| id | user_id | food_id | 주문일 |
|---|---|---|---|
| 1 | 1 | 1 | 2023-01-01 |
| 2 | 2 | 1 | 2023-01-01 |
| 3 | 2 | 2 | 2023-01-01 |
| 4 | 1 | 4 | 2023-01-01 |
| 5 | 2 | 3 | 2023-01-01 |
고객 1명은 음식 N개를 주문할 수 있습니다.
음식 1개는 고객 N명에게 주문될 수 있습니다.
결론적으로 고객과 음식은 N : M 관계입니다.
이렇듯 N : M 관계인 테이블들의 연관 관계를 해결하기 위해 orders 테이블처럼 중간 테이블을 사용할 수 있습니다.
고객 1명은 주문을 여러번 할 수 있습니다.
- 고객 : 주문 = 1 : N
음식 1개는 주문이 여러번 될 수 있습니다.
- 음식 : 주문 = 1 : N
SELECT u.name as username, f.name as foodname, o.order_date as orderdate
FROM users u
INNER JOIN orders o on u.id = o.user_id
INNER JOIN food f on o.food_id = f.id
WHERE o.user_id = 1;SELECT u.name as username, f.name as foodname, o.order_date as orderdate
FROM food f
INNER JOIN orders o on f.id = o.food_id
INNER JOIN users u on o.user_id = u.id
WHERE o.user_id = 1;@Entity
@Table(name = "food")
public class Food {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private double price;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
}@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "user")
private List<Food> foodList = new ArrayList<>();
}한명의 고객은 여러번 주문이 가능한 상황입니다.
- 이를 Entity에서 여러번 가능함을 표현하기 위해 Java Collection을 사용하여 List<Food> foodList = new ArrayList<>() 이처럼 표현할 수 있습니다.
그렇다면 Entity에서 이렇게까지 해서 표현을 하는 이유가 무엇일까요?
DB 테이블에서는 고객 테이블 기준으로 음식의 정보를 조회하려고 할 때 JOIN을 사용하여 바로 조회가 가능하지만 고객 Entity 입장에서는 음식 Entity의 정보를 가지고 있지 않으면 음식의 정보를 조회할 방법이 없습니다.
따라서 DB 테이블에 실제 컬럼으로 존재하지는 않지만 Entity 상태에서 다른 Entity를 참조하기 위해 이러한 방법을 사용합니다.
현재 음식 Entity와 고객 Entity는 서로를 참조하고 있습니다.
음식
@Entity
@Table(name = "food")
public class Food {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private double price;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
}
고객
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
음식 Entity에서만 고객 Entity를 참조할 수 있습니다.
<정리>
따라서 Entity에서는 DB 테이블에는 없는 방향의 개념이 존재합니다.
DB 테이블에는 이 방향이라는 개념이 없지만 Entity 클래스 세계에서는 객체 형태이기 때문에 서로 참조하기 위해 상대 엔티티의 타입을 필드로 이렇게 가지고 있어야만 합니다.
그렇기 때문에 그런 참조할 만한 필드가 없다면 조회가 불가능한 상황이 발생을 해서 방향이라는 개념이 생기게 되었다.
그래서 서로 상대방의 엔티티를 참조하고 있다면 양방향.
한쪽이라도 참조하지 못하고 있다면 단방향이란 표현을 사용한다라고 정리를 하면 될 것 같다.