Spring/팀스파르타

38. Entity 연관관계 - 1 대 N

열심히 해 2024. 12. 6. 14:54

@OneToMany

  • @OneToMany 애너테이션은 1 대 N 관계를 맺어주는 역할을 합니다.
  • 음식 Entity와 고객 Entity가 1 대 N 관계라 가정하여 관계를 맺어보겠습니다.

 

 

단방향

 



코드에서 외래 키를 관리하는 주인은 음식 Entity이지만, 데이터  베이스에서 실제 외래 키는 고객 Entity가 가지고 있습니다.

 

>>  음식 Entity가 @JoinColum 에너테이션이 붙은 필드를 가지고 있습니다.

 

  • 1 : N에서 N 관계의 테이블이 외래 키를 가질 수 있기 때문에 외래 키는 N 관계인 users 테이블에 외래 키 컬럼을 만들어 추가합니다.
  • 하지만 외래 키의 주인이 음식 Entity이기 때문에, 음식 Entity가 고객 테이블의 외래 키 컬럼을 관리합니다.

 

단방향

import java.util.ArrayList;

@Entity
@Table(name = "food")
public class Food {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private double price;

    @OneToMany
    @JoinColumn(name = "food_id") // users 테이블에 food_id 컬럼
    private List<User> userList = new ArrayList<>();
}
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
}

 

 

  • 외래 키를 음식 Entity가 직접 가질 수 있다면 INSERT 발생 시 한번에 처리할 수 있지만 실제 DB에서 외래 키를 고객 테이블이 가지고 있기 때문에 추가적인 UPDATE가 발생된다는 단점이 존재합니다.

 

 

양방향

 

  • 1 대 N 관계에서는 일반적으로 양방향 관계가 존재하지 않습니다.
  • 1 대 N 관계에서 양방향 관계를 맺으려면 음식 Entity를 외래 키의 주인으로 정해주기 위해 고객 Entity에서 mappedBy 옵션을 사용해야 하지만 @ManyToOne 애너테이션은 mappedBy 속성을 제공하지 않습니다.
  • N 관계의 Entity인 고객 Entity에서 @JoinColum의 insertable 과 updatable 옵션을 false로 설정하여 양쪽으로 JOIN 설정을 하면 양방향처럼 설정할 수는 있습니다.
    >> 외래 키의 주인이 아닌 Entity가 외래 키를 관리할 수 없기 때문입니다.

 

 

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @ManyToOne
    @JoinColumn(name = "food_id", insertable = false, updatable = false)
    private Food food;
}

 

 

 

 


 

 

테스트

 

@Test
@Rollback(value = false)
@DisplayName("1대N 단방향 테스트")
void test1() {
    User user = new User();
    user.setName("Robbie");

    User user2 = new User();
    user2.setName("Robbert");

    Food food = new Food();
    food.setName("후라이드 치킨");
    food.setPrice(15000);
    food.getUserList().add(user); // 외래 키(연관 관계) 설정
    food.getUserList().add(user2); // 외래 키(연관 관계) 설정

    userRepository.save(user);
    userRepository.save(user2);
    foodRepository.save(food);

    // 추가적인 UPDATE 쿼리 발생을 확인할 수 있습니다.
}

 

 

@Test
@DisplayName("1대N 조회 테스트")
void test2() {
    Food food = foodRepository.findById(1L).orElseThrow(NullPointerException::new);
    System.out.println("food.getName() = " + food.getName());

    // 해당 음식을 주문한 고객 정보 조회
    List<User> userList = food.getUserList();
    for (User user : userList) {
        System.out.println("user.getName() = " + user.getName());
    }
}