bookStore과 book가 일대다 양방향 연관 관계를 맺고 있는 상황을 생각해보자. entity 코드는 아래와 같다.
(entity에 @setter는 일반적으로 쓰지 않는 것이 좋지만 JPA 일대다 양방향 연관 관계 맵핑 시 주의할 점을 설명하기 위해 선언하였다.)
@Entity
@Getter @Setter
public class BookStore {
@Id @GenerateValue
private Integer id;
private String name;
@OneToMany(mappedBy = "bookStore")
private Set<Book> books = new HashSet<>();
public void add(Book book) {
this.books.add(book);
}
}
@Entity
@Getter @Setter
public class Book {
@Id @GeneratedValue
private Integer id;
private String isbn;
private String title;
@ManyToOne
private BookStore bookStore;
}
그러나 아래 테스트 코드를 실행하면
@RunWith(SpringRunner.class)
@SpringBootTest
public class JpaTestApplicationTests {
@Autowired
BookStoreRepository bookStoreRepository;
@Autowired
BookRepository bookRepository;
@Test
public void contextLoads() {
BookStore bookStore = new BookStore();
bookStore.setName("시애틀 책방");
bookStoreRepository.save(bookStore);
Book book = new Book();
book.setTitle("JPA 공부 좀 하면서 쓰세요.");
bookStore.add(book)
bookRepository.save(book)
}
}
bookStore entity에는 의도대로 저장이 아래와 같이 되었지만
book entity에서 book_store_id가 null이 찍힘을 확인할 수 있다.
막상 DB를 보면 book과 bookStore은 연결이 되지 않은 상황이었던 것이다.
위 엔티티 코드에서 어디가 잘못되었을까?
위의 엔티티 코드는 @OneToMany의 양방향 연관관계, MappedBy를 잘 모르고 설계된 코드이다.
기본적으로 필드로 서로의 클래스를 참조하여 @OneToMany, @ManyToOne 어노테이션을 각자 클래스에서 작성했다고 하더라도 mappedBy가 없다면 이 둘은 서로 다른 두 개의 단방향 관계이다. 여기에 mappedBy로 일대다를 엮어줘야지만 양방향 관계가 된다.
(하지만 양방향을 만들기 위해 일대다에서 "다"에 해당되는 것을 관계의 주인으로 만들어주는 mappedBy만 이용되는 것은 아니고 "일"에 해당하는 것을 주인으로 만들어주는 다른 방법도 있다.)
@OneToMany(mappedBy = "bookStore")
private Set<Book> books = new HashSet<>();
mappedBy를 통해서 위 bookStore class에서 관계의 주인이 Book이고 bookStore에서 자기 자신을 bookStore로 참조하고 있게 만들어준다. (mappedBy는 관계의 주인이 아닌 쪽(클래스)에 붙는다.) 즉 관계의 주인인 Book에서 관계가 설정되어주어야만!!! 일대다에서 다 == 관계의 주인인 테이블 book에서 양방향 관계를 맺은 일대다에서 일에 해당하는 상대측 bookStore 외래키(여기서는 book_store_id)가 적용이 된다.
다시 말해, 기존 bookStore 코드에서는 관계의 주인이 book인데 book에서 관계를 설정하지 않고 add 함수에서 자신한테만 관계를 설정하고 있었으며 book 쪽에서 아무런 관계에 대한 조정이 없었기 때문에 book table에서 book store 외래키가 null로 찍힌 것이다.
그렇다면 관계의 주인인 일대다에서 다에 해당하는 book에서 관계를 설정해준다는 것이 무슨 의미일까?
bookStore 클래스를 아래와 같이 수정해보자.
@Entity
@Getter @Setter
public class BookStore {
@Id @GenerateValue
private Integer id;
private String name;
@OneToMany(mappedBy = "bookStore")
private Set<Book> books = new HashSet<>();
public void add(Book book) {
book.setBookStore(this); // 이 코드를 추가한다
this.books.add(book); // == getBooks().add(book); 이 코드만 있을 때 DB에 일대다 관계 반영이 안되더라도 객체적인 코드 설계를 위해 양쪽에서 관계 세팅을 해주는 것이 바람직하다
}
}
book.setBookStore(this);를 추가함으로써 book 쪽 == 양방향 연관관계의 주인 쪽에서 관계 세팅을 해주어야지만 DB에 반영이 된다.
이제 아래와 같이 맵핑이 정상적으로 된다.
OneToMany, mappedBy... 뭐든지 기초가 가장 중요함을 알 수 있었던 시간이었다!
스스로에게 하는 말... JPA 공부 좀 하면서 쓰겠습니다..
'SpringBoot > 오개념 때려잡기' 카테고리의 다른 글
[Spring] JPA @NotNull @NonNull nullable=false 차이와 대안 (1) | 2025.04.28 |
---|---|
[Spring] @EntityScan은 언제 필요할까 (0) | 2025.04.27 |
[Spring] SecurityFilterChain 구성 요소 (0) | 2024.09.01 |
[Spring] @NoArgsConstructor (access = AccessLevel.PROTECTED)의 타당성 (0) | 2024.08.11 |