티스토리 뷰
JPA 유튜브 중에 아래와같은 동영상이 있어 이 문제를 해결해보고자한다.
https://www.youtube.com/watch?v=brE0tYOV9jQ&t=156s
먼저 방송에서도 언급하지만 코드베이스는 매우작다. BOOK 엔티티와 BOOK_STORE 엔티티 두개만 존재하고 이 둘은 BOOK(N) : BOOK_STORE(1) 관계이다.
@Entity
@Getter
@Setter
public class Book {
@Id @GeneratedValue
@Column(name = "BOOK_ID")
private Long id;
@Column(name = "ISBN")
private String isbn;
@Column(name = "TITLE")
private String title;
@ManyToOne
private BookStore bookStore;
}
@Entity
@Getter
@Setter
public class BookStore {
@Id @GeneratedValue
@Column(name = "BOOKSTORE_ID")
private Long id;
@Column(name = "NAME")
private String name;
@OneToMany(mappedBy = "bookStore")
private Set<Book> books = new HashSet<>();
public void add(Book book) {
books.add(book);
}
}
그리고 엔티티는 서로 양방향으로 참조를 하고있다.
해당 엔티티를 이용한 테스트코드 역시 간단하다.
@SpringBootTest
public class JpaTest {
@Autowired
private BookStoreRepository bookStoreRepository;
@Autowired
private BookRepository bookRepository;
@Test
public void test() {
BookStore bookStore = new BookStore();
bookStore.setName("책방");
bookStoreRepository.save(bookStore);
Book book = new Book();
book.setTitle("JPA");
bookStore.add(book);
bookRepository.save(book);
}
}
테스트코드를 돌려보면 잘 저장이 되는걸 볼수있다. 다만 BOOK 테이블에 BOOK_STORE 와의 관계를 이어주는 외래키가 정상적으로 저장되지않는다.
이는 JPA 의 일대다 양방향 연관관계를 잘못사용해 발생하는 문제이다. 또한 객체와 관계형의 차이에서 발생하는 임피던스 불일치라고도 볼 수 있는데 RDB 에서는 한쪽에(1:N 관계에서 N쪽에) 외래키를 저장해두고 어느쪽에서는 반대편을 참조할 수 있다. 하지만 객체관계에서는 양방향 참조를 위해서는 서로가 서로를 참조로 갖고있어야한다. 이 차이때문에 bookStore(1) 에서 book(N) 을 넣어두고 저장하는 문제가 발생한것이다.
다시 RDB 관점에서 생각해보자. RDB 관계에서 외래키는 N 이 보유하게된다. 즉 bookStore 가 book 을 갖고있는게 아니라 book 이 bookStore 를 들고있어야한다.
@SpringBootTest
public class JpaTest {
@Autowired
private BookStoreRepository bookStoreRepository;
@Autowired
private BookRepository bookRepository;
@Test
public void test() {
BookStore bookStore = new BookStore();
bookStore.setName("책방");
bookStoreRepository.save(bookStore);
Book book = new Book();
book.setTitle("JPA");
// 변경부분
book.setBookStore(bookStore);
bookRepository.save(book);
}
}
코드를 변경하고 실행하면 정상적으로 외래키가 들어간걸 볼 수 있다.
JPA 에서 @OneToMany 애노테이션을 사용하면 mappedBy 속성을 지정해주게된다. 상대편의 어떤 엔티티에 의해 매핑될것인지를 명시해주는것이다. 그리고 1:N 관계에서 연관관계의 주인은 N이 되게된다. 때문에 mappedBy 를 이용한 필드는 JPA 에서 read-only 로 이용되어 실제 insert 시에는 insert 되지않는다.
이는 알고있는 상태에서 JPA 를 쓸때도 아무생각없이 잘 발생할 수 있는 실수이므로 테이블의 제약조건을 잘 걸어놓거나(nullable), 연관관계를 이어주는 메서드를 이용하는등의 습관을 들이는게 좋을것같다.
'Java > jpa' 카테고리의 다른 글
spring boot 2.x hibernate 5.x 에서 매핑키만 있고 데이터는 없는 경우 전체 엔티티 조회 실패 (0) | 2024.04.30 |
---|---|
Entity Merge 시에는 동일한 객체를 반환하지 않는 이유 (0) | 2023.05.07 |
@DynamicUpdate 는 언제 써야할까 (0) | 2023.01.10 |
non-primary key 를 이용해 연관관계 설정시 Serializable 인터페이스 구현 (1) | 2021.06.16 |
JPA 조회조건 객체 매핑 (0) | 2017.12.15 |
- Total
- Today
- Yesterday
- 정규표현식
- java8
- Design Pattern
- JPA
- code
- clean code
- Jackson
- backend개발환경
- frontcode
- TEST
- java
- javascript
- MySQL
- generics
- mariadb
- OOP
- db
- EffectiveJava
- DesignPattern
- frontend개발환경
- JavaScript Core
- Spring
- programming
- spring cloud
- go-core
- Kotlin
- toby
- http
- servlet
- Git
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |