1대1 관계(@OneToOne)

Sunghun Kim·2024년 11월 5일

Jpa

목록 보기
6/10

@OneToOne

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

1. 단방향 관계

외래 키의 주인 정하기(@JoinColumn())

  • Entity에서 외래 키의 주인은 일반적으로 N(다)의 관계인 Entity 이지만 1 대 1 관계에서는 외래 키의 주인을 직접 지정해야합니다.

외래 키 주인만이 외래 키등록, 수정, 삭제할 수 있으며, 주인이 아닌 쪽은 오직 외래 키를 읽기만 가능합니다.

  • @JoinColumn()은 외래 키의 주인이 활용하는 어노테이션입니다.
  • 컬럼명, null 여부, unique 여부 등을 지정할 수 있습니다.

음식 Entity가 외래 키의 주인인 경우!

  • 음식

       @Entity
       @Table(name = "food")
       public class Food {
           @Id
           @GeneratedValue(strategy = GenerationType.IDENTITY)
           private Long id;
           private String name;
           private double price;
       
           @OneToOne
           @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
     @Table(name = "food")
     public class Food {
         @Id
         @GeneratedValue(strategy = GenerationType.IDENTITY)
         private Long id;
         private String name;
         private double price;
     }
    • 고객
      @Entity
      @Table(name = "users")
      public class User {
          @Id
          @GeneratedValue(strategy = GenerationType.IDENTITY)
          private Long id;
          private String name;
      
          @OneToOne
          @JoinColumn(name = "food_id")
          private Food food;
      }

2. 양방향 관계

  • 양방향 설정
  • 양방향 관계에서 외래 키의 주인을 지정해 줄 때 mappedBy 옵션을 사용합니다.
  • 외래키의 주인을 지정한다는 것은, 객체의 두 관계 중 제어의 권한(데이터 조회, 저장, 수정, 삭제)를 갖는 실질적인 관계가 무엇인지 JPA에게 알리는 것이다.
  • 따라서 연관관계의 주인은 연관 관계를 갖는 두 객체 사이에서 조회, 저장, 수정, 삭제를 할 수 있지만, 주인이 아니면 조회만 가능하다.
  • mappedBy의 속성값은 외래 키의 주인인 상대 Entity의 필드명을 의미합니다.
  • 연관관계의 주인이 아닌 객체는 mappedBy 속성을 사용해 주인 필드의 변수명을 지정해주면 된다.

관계 설정 방법

  • 단방향이라면 외래 키의 주인만 상대 Entity 타입의 필드를 가지면서 @JoinColumn()을 활용하여 외래 키의 속성을 설정해주면됩니다.

  • 양방향이라면 외래 키의 주인은 상대 Entity 타입의 필드를 가지면서 @JoinColumn()을 활용하여 외래 키의 속성을 설정을 해줍니다.

  • 그리고 상대 Entity는 외래 키의 주인 Entity 타입의 필드를 가지면서 mappedBy 옵션을 사용하여 속성 값으로 외래 키의 주인 Entity에 선언된 @JoinColumn()으로 설정되고 있는 필드명을 넣어주면 됩니다.

음식 Entity가 외래 키의 주인인 경우!

  • 음식
     @Entity
     @Table(name = "food")
     public class Food {
         @Id
         @GeneratedValue(strategy = GenerationType.IDENTITY)
         private Long id;
         private String name;
         private double price;
     
         @OneToOne
         @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;
    
        @OneToOne(mappedBy = "user")
        private Food food;
    }

고객 Entity가 외래 키의 주인인 경우!

  • 음식
     @Entity
     @Table(name = "food")
     public class Food {
         @Id
         @GeneratedValue(strategy = GenerationType.IDENTITY)
         private Long id;
         private String name;
         private double price;
     
         @OneToOne(mappedBy = "food")
         private User user;
     }
  • 고객
       @Entity
       @Table(name = "users")
       public class User {
           @Id
           @GeneratedValue(strategy = GenerationType.IDENTITY)
           private Long id;
           private String name;
       
           @OneToOne
           @JoinColumn(name = "food_id")
           private Food food;
       }

@Transactional 어노테이션을 걸거나 걸지 않아야되는 상황(중요)

 @Test
 void test() {
 	Food food new Food();
 	food.setName("양고기");
 	foodRepository.save(food);
 	
 	Food food2 = new Food();
 	food2.setPrice(101010);
 	foodRepository.save(food2);|
 }

name Column에 null이 들어가면 안된다는 nullable 속성 설정으로 오류를 발생시킨다.

  1. 위처럼 @Transactional이 없으면 첫번째 food 변수의 데이터는 저장이된다.
    ⇒ 로직에서 에러나기 전 데이터는 insert 되야 되는 상황일때

  2. 그러나 @Transactional이 붙어있으면 Transactional 환경이라 전체가 Rollback된다.
    ⇒ 로직에서 Transaction 상황처럼 하나의 동작으로 insert 되야 하는 상황일때

1대1 양방향 Test

외래키의 주인이 아닌 Entity에서 외래키의 주인 Entity 저장시

	@Test
    @Rollback(value = false)
    @DisplayName("1대1 양방향 테스트 : 외래 키 저장 실패")
    void test2() {
        Food food = new Food();
        food.setName("고구마 피자");
        food.setPrice(30000);

        // 외래 키의 주인이 아닌 User 에서 Food 를 저장해보겠습니다.
        User user = new User();
        user.setName("Robbie");
        user.setFood(food);

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

        // 확인해 보시면 user_id 값이 들어가 있지 않은 것(null)을 확인하실 수 있습니다.
    }

외래키의 주인이 아닌 Entity에서 외래키의 주인 Entity 메서드 추가 후 저장 가능

@Test
    @Rollback(value = false)
    @DisplayName("1대1 양방향 테스트 : 외래 키 저장 실패 -> 성공")
    void test3() {
        Food food = new Food();
        food.setName("고구마 피자");
        food.setPrice(30000);

        // 외래 키의 주인이 아닌 User 에서 Food 를 저장하기 위해 addFood() 메서드 추가
        // 외래 키(연관 관계) 설정 food.setUser(this); 추가
        User user = new User();
        user.setName("Robbie");
        user.addFood(food);

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

외래키의 주인이 아닌 Entity에 메서드 추가

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

    @OneToOne(mappedBy = "user")
    private Food food;
	
    // 여기서 외래키 주인 Entity food에 User를 저장하고 있어 가능하다. 
    public void addFood(Food food) {
        this.food = food;
        food.setUser(this);
    }

정리

    1. JoinColumn을 쓰면 실제로 name에 들어가는 게 컬럼의 이름이 되고 DB에도 외래키로 설정이 된다.

    2. 외래키의 주인은 곧 테이블에 실제 존재하는 외래키의 위치라고도 생각하시면 좀 이해가 더 잘 되실 것 같습니다.

    3. 단방향일때 음식 Entity가 외래키의 주인인 경우 고객 Entity쪽에서는 음식 Entity를 조회할 수 없어요. 데이터베이스 테이블에서는 가능하지만 Entity에서는 불가능하다.

    4. JPA에서는 양방향일때 외래키의 주인(연관관계의 주인)을 지정해줘야된다.(내부적으로 그렇게 구현되어 있다.)

    5. mappedBy 옵션이 외래키의 주인이 아닌쪽에서 지정을 한다.

    6. 양방향일 때 mapped by 옵션을 사용해서 외래키의 주인을 우리가 지정을 해줘야 되는데 mapped by 옵션은 외래키의 주인이 아닌 Entity에서 설정을 하고 그 속성의 값은 외래키의 주인인 상대 Entity의 필드명을 의미한다.

      외래키의 주인인 타입의 필드를 가지면서 mappedBy 속성 값으로 외래키의 주인에 선언된 @JoinColumn으로 설정되고 있는 필드명을 넣어주면 된다.

    7. 외래키의 주인을 지정한다는 것은, 객체의 두 관계 중 제어의 권한(데이터 조회, 저장, 수정, 삭제)를 갖는 실질적인 관계가 무엇인지 JPA에게 알리는 것이다.

@MappedBy 생략시 주의점

양방향 관계에서 1대1 관계의 경우 각 테이블에 서로를 참조하는 FK가 설정된다.

profile
BackEnd Developer!!

0개의 댓글