@OneToOne@OneToOne 애너테이션은 1 대 1 관계를 맺어주는 역할을 합니다.외래 키 주인만이 외래 키 를 등록, 수정, 삭제할 수 있으며, 주인이 아닌 쪽은 오직 외래 키를 읽기만 가능합니다.
음식
@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
@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;
}
- 외래키의 주인을 지정한다는 것은, 객체의 두 관계 중 제어의 권한(데이터 조회, 저장, 수정, 삭제)를 갖는 실질적인 관계가 무엇인지 JPA에게 알리는 것이다.
- mappedBy의 속성값은 외래 키의 주인인 상대 Entity의 필드명을 의미합니다.
- 연관관계의 주인이 아닌 객체는 mappedBy 속성을 사용해 주인 필드의 변수명을 지정해주면 된다.
단방향이라면 외래 키의 주인만 상대 Entity 타입의 필드를 가지면서 @JoinColumn()을 활용하여 외래 키의 속성을 설정해주면됩니다.
양방향이라면 외래 키의 주인은 상대 Entity 타입의 필드를 가지면서 @JoinColumn()을 활용하여 외래 키의 속성을 설정을 해줍니다.
그리고 상대 Entity는 외래 키의 주인 Entity 타입의 필드를 가지면서 mappedBy 옵션을 사용하여 속성 값으로 외래 키의 주인 Entity에 선언된 @JoinColumn()으로 설정되고 있는 필드명을 넣어주면 됩니다.
@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
@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;
} @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 속성 설정으로 오류를 발생시킨다.
위처럼 @Transactional이 없으면 첫번째 food 변수의 데이터는 저장이된다.
⇒ 로직에서 에러나기 전 데이터는 insert 되야 되는 상황일때
그러나 @Transactional이 붙어있으면 Transactional 환경이라 전체가 Rollback된다.
⇒ 로직에서 Transaction 상황처럼 하나의 동작으로 insert 되야 하는 상황일때
@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)을 확인하실 수 있습니다.
}
@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
@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);
}
JoinColumn을 쓰면 실제로 name에 들어가는 게 컬럼의 이름이 되고 DB에도 외래키로 설정이 된다.
외래키의 주인은 곧 테이블에 실제 존재하는 외래키의 위치라고도 생각하시면 좀 이해가 더 잘 되실 것 같습니다.
단방향일때 음식 Entity가 외래키의 주인인 경우 고객 Entity쪽에서는 음식 Entity를 조회할 수 없어요. 데이터베이스 테이블에서는 가능하지만 Entity에서는 불가능하다.
JPA에서는 양방향일때 외래키의 주인(연관관계의 주인)을 지정해줘야된다.(내부적으로 그렇게 구현되어 있다.)
mappedBy 옵션이 외래키의 주인이 아닌쪽에서 지정을 한다.
양방향일 때 mapped by 옵션을 사용해서 외래키의 주인을 우리가 지정을 해줘야 되는데 mapped by 옵션은 외래키의 주인이 아닌 Entity에서 설정을 하고 그 속성의 값은 외래키의 주인인 상대 Entity의 필드명을 의미한다.
외래키의 주인인 타입의 필드를 가지면서 mappedBy 속성 값으로 외래키의 주인에 선언된 @JoinColumn으로 설정되고 있는 필드명을 넣어주면 된다.
외래키의 주인을 지정한다는 것은, 객체의 두 관계 중 제어의 권한(데이터 조회, 저장, 수정, 삭제)를 갖는 실질적인 관계가 무엇인지 JPA에게 알리는 것이다.
양방향 관계에서 1대1 관계의 경우 각 테이블에 서로를 참조하는 FK가 설정된다.