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()으로 설정되고 있는 필드명을 넣어주면 된다.
- 양방향 관계에서 외래 키의 주인을 지정해 줄 때 mappedBy 옵션을 사용한다
- 양방향 설정
음식 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());
}
}