본문 바로가기

개발 일지/TIL

[ #28 ] TIL

✏️ 0524      


Spring 숙련 4주차 강의

밍글데이🤍

번외 : 자료구조 강의


Spring 숙련 4주차

영상과 내용이 넘므 많아효...

 

Entity 연관 관계


데이터베이스에서는 어떤 테이블을 기준으로 하든지 간에 원하는 정보를 JOIN 을 사용해서 조회를 할 수 있다
그래서 데이터베이스 관계에서는 방향이라는 개념이 없다
JOIN 이나 WHERE 을 통해서 양방향으로 조회가 가능하기 때문에 방향이 없다고 하는게 더 맞다

 

 

JPA, 테이블과 맵핑이 되는 Entity 클래스들은 어떻게 연관 관계를 표현하고 있는가!

@Entity
@Table(name = "food")
public class Food {
	...
    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;
}


N (Food)  : 1 (User) 관계일 때
음식에서는 고객과 연관관계를 맺기 위해서 상대방 entity의 필드를 갖는다 

 

 

@Entity
@Table(name = "users")
public class User {
	...
    @OneToMany(mappedBy = "user")
    private List<Food> foodList = new ArrayList<>();
}


고객은 음식과 연관관계를 맺기 위해 상대방 entity의 필드를 갖는데
N : 1 관계이므로 데이터가 여러개 들어올 수 있으니 자바의 컬렉션을 사용할 수 있다

이때 리스트는 실제로 저장되는 데이터는 아니고 조회를 위한 표현이다
그러니, 데이터베이스 테이블에 영향을 미치지 않는다

DB 테이블에 실제 컬럼으로 존재하지는 않지만 다른 Entity를 참조하기 위해 이러한 방법을 사용한

위의 예시는 서로 연관관계를 맺고 있어서 N : 1 양방향 관계라고 하며
양쪽으로 서로 참조하면서 조회할 수 있는 상황이다


@Entity
@Table(name = "food")
public class Food {
	...
    @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;
}


위의 코드는 단방향 관계, 한쪽에서만 조회가 가능한 상태이다


서로 참조할 수 있었던 양방향 관계와는 다르게 음식에서만 고객에 대한 정보를 조회할 수 있다
고객은 음식에 대한 정보가 없기 때문에 조회가 불가능하다

 

  • 📝 정리
    • DB 테이블에서는 테이블 사이의 연관관계를 Foreign Key (외래 키) 로 맺을 수 있고
      방향 상관없이 조회가 가능하다
    • Entity 에서는 상대 Entity 를 참조하여 Entity 사이의 연관관계를 맺을 수 있다
      하지만 상대 Entity 를 참조하지 않고 있다면 상대 Entity 를 조회할 수 있는 방법은 없다
    • Entity 에서는 DB 테이블에 없는 단방향, 양방향의라는 방향의 개념이 존재한다

 

 

1 : 1 관계

 

@OneToOne

1 대 1 관계를 맺어주는 역할

 

  • 단방향

Entity에서 외래 키의 주인은 일반적으로 N(다)의 관계인 Entity 이지만

1 대 1 관계에서는 외래 키의 주인을 직접 지정해야 한다

 

외래 키 주인만이 외래 키 를 등록, 수정, 삭제할 수 있으며

주인이 아닌 쪽은 오직 외래 키를 읽기만 가능

 

// Food
@OneToOne // 1:1
@JoinColumn(name = "user_id")
private User user;

 

@JoinColumn() 은 외래 키의 주인이 활용하는 애너테이션
컬럼명, null 여부, unique 여부 등을 지정할 수 있다

 

 

  • 양방향

양방향 관계에서 외래 키의 주인을 지정해 줄 때 mappedBy 옵션을 사용

mappedBy 의 속성값은 외래 키의 주인인 상대 Entity의 필드명을 의미한다

 

외래 키의 주인은 상대 Entity 타입의 필드를 가지면서 @JoinColumn()을 활용하여 외래 키의 속성을 설정
상대 Entity는 외래 키의 주인 Entity 타입의 필드를 가지면서 mappedBy 옵션을 사용하여

속성 값으로 외래 키의 주인 Entity에 선언된 @JoinColumn()으로 설정되고 있는 필드명을 넣어주면 된다

 

// Food
@OneToOne // 1:1
@JoinColumn(name = "user_id")
private User user;

// User
@OneToOne(mappedBy = "user")  // user 는 외래키의 주인인 상대 Entity 에 있는 필드명
private Food food;

public void addFood(Food food) {
    this.food = food;
    food.setUser(this);
}

 

외래 키의 주인 Entity에서 @JoinColumn() 애너테이션을 사용하지 않아도

default 옵션이 적용되기 때문에 생략 가능


다만 1 대 N 관계에서 외래 키의 주인 Entity가 @JoinColumn() 애너테이션을 생략한다면

JPA가 외래 키를 저장할 컬럼을 파악할 수가 없어서 의도하지 않은 중간 테이블이 생성된다


따라서 외래 키의 주인 Entity에서 @JoinColumn() 애너테이션을 활용하시는게 좋다
생략은 하지않는걸 추천 !!

 

양방향 관계에서 mappedBy 옵션을 생략할 경우

JPA가 외래 키의 주인 Entity를 파악할 수가 없어 의도하지 않은 중간 테이블이 생성되기 때문에

반드시 설정해주시는게 좋다

 

외래키의 주인만이 외래키를 컨트롤할 수 있다

 

 

N : 1 관계

 

@ManyToOne

N 대 1 관계를 맺어주는 역할


  • 단방향
// Food
@ManyToOne  // N:1
@JoinColumn(name = "user_id")
private User user;

 

 

  • 양방향
// Food
@ManyToOne
@JoinColumn(name = "user_id")
private User user;

// User
@OneToMany(mappedBy = "user")
private List<Food> foodList = new ArrayList<>();

public void addFoodList(Food food) {
    this.foodList.add(food);
    food.setUser(this); // 외래 키(연관 관계) 설정
}

 

 

 

1 : N 관계

 

@OneToMany

1 대 N 관계를 맺어주는 역할

 

데이터베이스에서는 N에 관계에 있는 테이블이 외래키를 가지고있다
그리고 Entity 에서도 N에 관계에 있는 테이블이 외래키를 가지고있다

  • 단방향
// 음식 Entity와 고객 Entity가 1 대 N 관계라 가정
// Food
@OneToMany // 1:N
@JoinColumn(name = "food_id") // users 테이블에 food_id 컬럼
private List<User> userList = new ArrayList<>();

 

외래키의 주인은 음식 Entity 이지만 실제 데이터베이스에서는 외래키를 고객이 가지고 있는 형태
그렇기에 Entity 에서 실제 외래키를 계쏙 컨트롤 하는 거는 음식의 Entity의 List 가 된다
외래키 설정을 List 필드를 통해서 해야한다

 

외래키를 음식 Entity 가 직접 가질 수 있다면 INSERT 발생 시 한번에 처리할 수 있지만

실제 DB에서 외래 키를 고객 테이블이 가지고 있기 때문에 추가적인 UPDATE가 발생된다는 단점이 존재

내 데이터지만 타인의 창고를 빌려서 그 창고 안에 넣고 그걸 내가 관리하는 느낌

 

 

  • 양방향
// User
@ManyToOne
@JoinColumn(name = "food_id", insertable = false, updatable = false)
private Food food;

 

1 대 N 관계에서는 일반적으로 양방향 관계가 존재하지 않는다
1 대 N 관계에서 양방향 관계를 맺으려면 음식 Entity를 외래 키의 주인으로 정해주기 위해

고객 Entity에서 mappedBy 옵션을 사용해야 하지만 @ManyToOne 은 mappedBy 속성을 제공하지 않는다

 

N 관계의 Entity인 고객 Entity에서 @JoinColum의 insertable 과 updatable 옵션을 false로 설정하여

양쪽으로 JOIN 설정을 하면 양방향처럼 설정은 할 수는 있지만 추천하지는 않는다


 

N : M 관계

 

@ManyToMany

N 대 M 관계를 맺어주는 역할

 

  • 단방향
// Food
@ManyToMany // N : M
@JoinTable(name = "orders", // 중간 테이블 생성
    // 현재 위치인 Food Entity 에서 중간 테이블로 조인할 컬럼 설정
    joinColumns = @JoinColumn(name = "food_id"),
    // 반대 위치인 User Entity 에서 중간 테이블로 조인할 컬럼 설정
    inverseJoinColumns = @JoinColumn(name = "user_id"))

private List<User> userList = new ArrayList<>();

 

@JoinTabel(name="orders") 을 사용해서 중간 테이블을 생성하고 연결해준다 이름을 orders 라고 지정
직접 만든 테이블이 아닌 JPA 에 의해 자동으로 만들어진 테이블은 PK가 없고 컨트롤 하기 어렵다 
그렇기에 추후에 중간 테이블의 변경이 발생할 경우 문제가 발생할 가능성이 있다

 

  • 양방향
// Food
@ManyToMany // N : M
@JoinTable(name = "orders", // 중간 테이블 생성
    // 현재 위치인 Food Entity 에서 중간 테이블로 조인할 컬럼 설정
    joinColumns = @JoinColumn(name = "food_id"),
    // 반대 위치인 User Entity 에서 중간 테이블로 조인할 컬럼 설정
    inverseJoinColumns = @JoinColumn(name = "user_id"))

private List<User> userList = new ArrayList<>();

// User
// N : M 양방향
@ManyToMany(mappedBy = "userList")
private List<Food> foodList = new ArrayList<>();

public void addFoodList(Food food) {
    this.foodList.add(food);
    food.getUserList().add(this); // 외래 키(연관 관계) 설정
}

 

반대 방향인 고객 Entity에 @ManyToMany 로 음식 Entity를 연결하고 mappedBy 옵션을 설정하여

외래 키의 주인을 설정하면 양방향 관계 맺음이 가능

 

 

  • 중간 테이블
// Food
// N:M 중앙 테이블을 직접 생성했을 때
@OneToMany(mappedBy = "food")
private List<Order> orderList = new ArrayList<>();

// User
// N:M 중앙 테이블을 직접 생성했을 때
// cascade = CascadeType.PERSIST 영속성 전이 추가 (한번에 저장)
// cascade = {CascadeType.PERSIST, CascadeType.REMOVE} 중복 설정 가능=> 중괄호 사용, 한번에 삭제 추가
// orphanRemoval = true (default = false) : 연관관계 제거
@OneToMany(mappedBy = "user", cascade = CascadeType.PERSIST, orphanRemoval = true)
private List<Order> orderList = new ArrayList<>();

// cascadeTest
public void addFoodList(Food food) {
    this.foodList.add(food);
    food.setUser(this);  // 외래키 설정
}


JPA 에 의한 자동으로 만들어진 중간 테이블이 아닌
직접 생성하여 관리하면 변경 발생 시 컨트롤하기 쉽기 때문에 확장성에 좋다

외래키의 주인이 중앙 테이블이 되면서 외래키의 주인이 외래키를 가지고 있고 효율적이다
order 테이블을 통해서 food가 user 조회가 가능하고 user가 food 조회도 가능하게 된다
만약 food 쪽에서 order 를 통해 user 를 조회할 필요가 없을 때
굳이 양방향일 필요가 없다

 

 

회고

 

밍글데이..

상품은 못 얻었지만 잼있었다

바글바글 해서 렉이 아주 짱이었다 ㅋㅋ

'개발 일지 > TIL' 카테고리의 다른 글

[ #30 ] TIL  (0) 2024.05.28
[ #29 ] TIL  (0) 2024.05.27
[ #27 ] TIL  (2) 2024.05.23
[ #26 ] TIL  (0) 2024.05.22
[ #25 ] TIL  (0) 2024.05.21