음식 : 고객 = N : 1
- 음식에서도 고객을 조회하고 고객도 음식을 조회할 수 있도록 하는 JPA Entity
@Entity
@Table
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;
}
@ManyToOne
: @ManyToOne이 선언된 Entity가 Many.
Food가 Many, User가 1.
@JoinColumn(name="user_id")
private User user;
: User user 객체는 user_id column을 참조한다.
@Entity
@Table
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mapped by "user")
private List<Food> foodList = new ArrayList<>();
}
List<Food> foodList = new ArrayList<>() 이처럼 표현할 수 있다.- 음식 Entity에서만 고객 Entity를 참조할 수 있다.
@Entity
@Table
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
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
@OneToOne
FK의 주인만이 FK 를 등록, 수정, 삭제할 수 있으며, 주인이 아닌 쪽은 오직 FK를 읽기만 가능하다.

음식 Entity가 FK의 주인인 경우!
@Entity
@Table(name="food")
public clas 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 clas 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;
}
OneToOne 단방향 테스트
: Food가 FK의 주인 entity
@Test
@Rollback(value = false) // 테스트에서는 @Transactional 에 의해 자동 rollback 됨으로 false 설정해준다.
@DisplayName("1대1 단방향 테스트")
void test1() {
User user = new User();
user.setName("Robbie");
// 외래 키의 주인인 Food Entity user 필드에 user 객체를 추가해 줍니다.
Food food = new Food();
food.setName("후라이드 치킨");
food.setPrice(15000);
food.setUser(user); // 외래 키(연관 관계) 설정
userRepository.save(user);
foodRepository.save(food);
}

음식 Entity가 외래 키의 주인인 경우!
@Entity
@Table(name = "food")
public class Food {
@Id
@GeneratedValue(strategy = GenrationType.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 = GenrationType.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;
양방향 OneToOne 테스트
Test2(실패)
: 외래 키의 주인은 Food인데 주인이 아닌 User에서 Food를 저장한다.
=> Food의 외래 키인 user_id값이 저장되지 않는다.
Test3(실패 -> 성공)
: 아래 고객 엔티티를 보면 addFood 메서드에 food를 주입받고 food가 setUser(this) 즉, user를 등록 = 외래키를 설정 해준다.
Test4(성공)
: 외래 키의 주인인 Food가
package com.sparta.jpaadvance.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
@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;
public void addFood(Food food) {
this.food = food;
food.setUser(this);
}
}
@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 값이 들어가 있지 않은 것을 확인하실 수 있습니다.
}
@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);
}
@Test
@Rollback(value = false)
@DisplayName("1대1 양방향 테스트")
void test4() {
User user = new User();
user.setName("Robbert");
Food food = new Food();
food.setName("고구마 피자");
food.setPrice(30000);
food.setUser(user); // 외래 키(연관 관계) 설정
userRepository.save(user);
foodRepository.save(food);
}
OneToOne 조회 테스트
@Test
@DisplayName("1대1 조회 : Food 기준 user 정보 조회")
void test5() {
Food food = foodRepository.findById(1L).orElseThrow(NullPointerException::new);
// 음식 정보 조회
System.out.println("food.getName() = " + food.getName());
// 음식을 주문한 고객 정보 조회
System.out.println("food.getUser().getName() = " + food.getUser().getName());
}
@Test
@DisplayName("1대1 조회 : User 기준 food 정보 조회")
void test6() {
User user = userRepository.findById(1L).orElseThrow(NullPointerException::new);
// 고객 정보 조회
System.out.println("user.getName() = " + user.getName());
// 해당 고객이 주문한 음식 정보 조회
Food food = user.getFood();
System.out.println("food.getName() = " + food.getName());
System.out.println("food.getPrice() = " + food.getPrice());
}