WTIL

TIL-15 JPA Entity 연관관계 1대1 관계

다오__ 2023. 6. 22. 19:51

DB 테이블에서는 테이블 사이의 연관관계를 FK로 맺을 수 있고 방향 상관없이 조회가 가능하다.

Entity에서는 상대 Entity를 참조하여 Entity사이의 연관관계를 맺을 수 있다.

하지만 상대 Entity를 참조하지 않고 있다면 상대 Entity를 조회할 수 있는 방법이 없다.

따라서 Entity에서는 DB테이블에는 없는 방향의 개념이 존재한다.

 


1:1 관계

@OneToOne 에너테이션은 1:1 관계를 맺어주는 역할을 한다

고객 Entity와 음식 Entity가 1:1관계라 가정하여 관계를 맺어보자

 

  • 단방향 관계
    • 외래키의 주인 정하기
      • Entity에서 외래키의 주인은 일단적으로 N의 관계인 Entity지만 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;
}

 

  • 양방향 관계
    • 양방향 설정
      • 양방향 관계에서 외래 키의 주인을 지정해 줄 때 mappedBy 옵션을 사용한다
        • mappedBy의 속성값은 외래 키의 주인인 상대 Entity의 필드명을 의미한다
      • 관계 설정 방법에 대해 정리
        • 단방향이라면 외래 키의 주인만 상대 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;
}

 

 

테스트코드로 연습

@SpringBootTest
@Transactional
public class OneToOneTest {

    @Autowired
    FoodRepository foodRepository;
    @Autowired
    UserRepository userRepository;


    @Test
    @Rollback(value = false)
    @DisplayName("1:1 단방향 테스트")
    void test1(){

        User user = new User();
        user.setName("kys");
        userRepository.save(user);

        Food food = new Food();
        food.setName("치킨");
        food.setPrice(15000);
        foodRepository.save(food);
        food.setUser(user);
    }

    @Test
    @Rollback(value = false)
    @DisplayName("1:1 단방향 조회 테스트")
    void test2(){
        //단방향에서는 기준 Entity만 다른 Entity를 조회할 수 있다.
        Food food = foodRepository.findById(1L).orElseThrow(NullPointerException::new);
        System.out.println("food.getUser().getName() = " + food.getUser().getName());

    }

    @Test
    @Rollback(value = false)
    @DisplayName("1:1 양방향 테스트")
    void test3(){
        User user = new User();
        user.setName("kys");


        Food food = new Food();
        food.setName("고구마피자");
        food.setPrice(15000);


        // = food.setUser(user)
        user.addFood(food);

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

    }

    @Test
    @DisplayName("1:1 양방향 조회 테스트 유저 기준")
    void test4(){
        //양방향에서는 user또한 조회가 가능하다.
        User user = userRepository.findById(1L).orElseThrow(NullPointerException::new);
        //고객 정보 조회
        System.out.println("user.getName() = " + user.getName());
        //고객이 주문한 음식 조회
        System.out.println("user.getFood().getName() = " + user.getFood().getName());
        System.out.println("user.getFood().getPrice() = " + user.getFood().getPrice());
    }

    @Test
    @DisplayName("1:1 양방향 조회 테스트 음식 기준")
    void test5(){
        Food food = foodRepository.findById(1L).orElseThrow(NullPointerException::new);
        //고객 정보 조회
        System.out.println("food.getUser().getName() = " + food.getUser().getName());

        //고객이 주문한 음식 조회
        System.out.println("food.getName() = " + food.getName());
        System.out.println("food.getPrice() = " + food.getPrice());

    }
}