N대1 관계(@ManyToOne) & 1대N 관계(@OneToMany)

Sunghun Kim·2024년 11월 5일

Jpa

목록 보기
7/10

@ManyToOne(N대1 관계)

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

단방향 관계

  • 음식 Entity가 N의 관계로 외래 키의 주인
    • 음식
      @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에서 Java 컬렉션을 사용하여 음식 Entity 참조
    • 음식 Entity가 N의 관계로 외래 키의 주인
    • 음식
      @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<>();
      }

보통 @ManyToOne어노테이션이 붙은 Entity가 외래키의 주인이다.

@OneToMany(1대N 관계)

💡 보통 일반적으로 1대N 의 관계에서 N인 쪽이 외래키의 주인이지만 여기에서는 조금 특이하다.
  • @OneToMany
    • @OneToMany 애너테이션은 1 대 N 관계를 맺어주는 역할을 합니다.
    • 음식 Entity와 고객 Entity가 1 대 N 관계라 가정하여 관계를 맺어보겠습니다.
    • 단방향이고 DB에 생기는 실제 컬럼을 표현한 것이 food_id이다.
    • 1 대 N 관계에서는 일반적으로 양방향 관계가 존재하지 않습니다.
    • 실제 외래키를 컨트롤하는 것은 List가 된다.(외래키의 주인이기에)
    • @JoinColumn에서 보통 N인 관계인 것이 외래키의 주인이라 user_id였지만 여기서는 user Table에 외래키가 저장되기 때문에 name = food_id로 설정한다.
    • 관리는 외래키의 주인이하고 외래키는 잠시 맡겨두는 느낌
    • 가끔가다 어쩔수 없이 이렇게 사용해야되는 때가 있다고 한다.(언제..?)

단방향 관계

  • 단방향 관계

  • 외래 키를 관리하는 주인은 음식 Entity이지만 실제 외래 키는 고객 Entity가 가지고 있습니다.

  • 위 그림은 DB에 고객테이블이 food_id(fk)를 가지고 있다는 것이고 Entity에는 없다.

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

  • 음식

           @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가 발생된다는 단점이 존재합니다.

  • List안에 User객체들이 있는 것을 Insert 후 food_id를 update 해준다.

  • userList에 User를 넣으면 일단 Insert가 날라가고 userList에 넣은 User의 정보로 food_id를 Update로 채워준다.

양방향 관계

  • 양방향 관계
    • 1 대 N 관계에서는 일반적으로 양방향 관계가 존재하지 않습니다.
      (JPA가 1대N 관계를 지원하지 않는다.)

    • 1 대 N 관계에서 양방향 관계를 맺으려면 음식 Entity를 외래 키의 주인으로 정해주기 위해 고객 Entity에서 mappedBy 옵션을 사용해야 하지만 @ManyToOne 애너테이션은 mappedBy 속성을 제공하지 않습니다.

      @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;
      }
    • N 관계의 Entity인 고객 Entity에서 @JoinColum의 insertable 과 updatable 옵션을 false로 설정하여 양쪽으로 JOIN 설정을 하면 양방향처럼 설정할 수는 있습니다.

    • 서로 참조하는데 외래키의 주인이 아닌 쪽에는 insert, update되면 안되니 (읽기만 가능) 위처럼 억지로 표현 할 수는 있다.(추천 X)

단방향 1대N Test

@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);

    // Insert 후 User테이블의 food_id의 추가적인 UPDATE 쿼리 발생을 확인할 수 있습니다.
}

mappedBy 옵션을 생략시 주의점

양방향 관계에서 mappedBy 옵션을 생략할 경우 JPA가 외래 키의 주인 Entity를 파악할 수가 없어 의도하지 않은 중간 테이블이 생성되기 때문에 반드시 설정해 주시는게 좋습니다.

  • N:1(ManyToOne) 관계의 경우 Join table이 생성

@JoinColumn() 어노테이션 생략시 주의점

  • 외래 키의 주인 Entity에서 @JoinColumn() 애너테이션을 사용하지 않아도 default 옵션이 적용되기 때문에 생략이 가능합니다.

    • 다만 1 대 N 관계에서 외래 키의 주인 Entity가 @JoinColumn() 어노테이션을 생략한다면 JPA가 외래 키를 저장할 컬럼을 파악할 수가 없어서 의도하지 않은 중간 테이블이 생성됩니다.
    • 따라서 외래 키의 주인 Entity에서 @JoinColumn() 애너테이션을 활용하시는게 좋습니다.
profile
BackEnd Developer!!

0개의 댓글