Back-end Skill/DataBase

JPA 사용할 때 주의해야할 점 2편_관계매핑의 정의와 mappedBy 이해하기

Sophie소피 2022. 10. 6. 21:52

그렇다면 양방향 관계 해결 방법은??

Entity01 BookStore

@Entity
@Getter @Setter 
public calss BookStore {
			@Id @GenerateValue
	private Integer id; 
	
	private String name; 

	@OneToMany(mappedBy ="bookStore")
	private Set<book> books = new HashSet<>();
			//add 메세지로 책을 책방에 추가한 것 
				void add(Book book){
				book.setBookStore(this);
				this.books.add(book);
			}
}

book.setBookStore(this); 의 의미는 무엇일까?
book.setBookStore(this); 를 추가하고 실행시키면
데이터 베이스 결과는

book database

id isbn title book_store_id

16 <null> JPA 공부 좀 하면서 쓰자 <null>
22 <null> JPA 공부 좀 하면서 쓰자 21

book_store_id 에 null에서 아이디가 설정된걸 확인할 수 있다.

book_store database

id name

25 소피책방
21 소피책방

책방도 두개가 생겼다.
근데 왜 생겼을까?
여기서 왜 생겼는지 이해하지 못했다면,
OneToMany와 mappedBy가 뭔지 모르는 것이다.
이 두개의 개념을 모르면 1탄에서 왜 에러가 나는지 찾을 수가 없을 것이다.

양방향 관계 해설

관계를 다시 처음부터 맺어보겠다.
북스토어에서도 북을 참조 북을 여러개 참조하여 OneToMany
북에서도 북스토어를 참조 ManyToOne
이 두개의 관계는 양방행 관계가 아닙니다.
이 두개는 서로 다른 두개의 단방향 관계입니다.

Entity01 BookStore

OneToMany컬렉션을 bookStore에서 book을 참조하는 또 다른 단방향관계 이다.

@Entity
@Getter @Setter 
public calss BookStore {
			@Id @GenerateValue
	private Integer id; 
	
	private String name; 

	@OneToMany
	private Set<book> books = new HashSet<>();
			//add 메세지로 책을 책방에 추가한 것 
				void add(Book book){
				this.books.add(book);
			}
}

Entity02 Book

이 관계는 book→ bookStore를 ManyToOne으로 보는 단방향 관계

@Entity
@Getter @Setter 

public calss Book {
		@Id @GenerateValue
		private Integer id; 

		private String isbn; 

		private String title; 

		@ManytoOne
		private BookStore;

}

이렇게 설정했을 때에는 스키마가 완전히 다르다.
테스트르 실행하면

	@ManytoOne
	private BookStore;

이 BookStore에 대해 해당하는 컬럼이 book이라는 테이블에
bookStore_id로 들어있다.
bookStore에서 book을 여러개 참조한
이관계는 기본적으로 조인테이블이 생성된다.
books_id와 bookStore_id가 조인된 테이블 생성된다.
이것이 기본 설정이다.
이게 hibernate jpa기본 Mapping방법이다.
OneToMany!
그런데 1편에서는

@OneToMany(mappedBy ="bookStore")

bookStore로 매핑이 되어있었다.
이걸함으로서 양방관계가 된거다.
그래도 주의할게 있다.
반드시 양방향 관게는 MappedBy로만 연결해야할까?
그렇지않다.
여러가지방법이 있는데, 이 두개의 OneToMany와 ManyToOne이 하나로 묶는 방법은
묶는 방법 중 하나가 지금 MappedBy를 사용해서 Entity02 book를 이 관계의 주인으로
설정하는 것이다. 1편처럼 mappedBy를 사용해서 스키마를 다시 만들면
테이블이 조인이 되지않고 2개만 나오게된다. Entity02 book쪽에서 외래키를 가지게 된다.
그래서 mappedBy의 의미는 관계의 주인을 나타내는 것이다.
이 관계의주인이 book이고, book에서 자기 자신을 bookStore이라고 참조하고 있다.
그러니까 Entity02 book이 주인이 되는 것이 무슨 뜻이지?
관계의 주인인 쪽에 관계가 설정해줘야한다.
그래야지만 데이터베이스에서 반영이 된다.
1편에서 작성한 그 코드들은 이상황에서 add를 할 때 어떻게 썻냐
.getBooks().add(book); 이렇게만 처리하였다.
이 관계의 주인은 book인데 book이 당하는 관계를 설정하지않고
현재 자기 자신한테만 관계를 설정하고 있는걸 알 수가 있다.
그러면 데이터베이스에 싱크를 할게 없는 것이다.
각기의 객체의 상태가 변경됐음에도 불구하고 데이터베이스에는 아무일도 일어나지않는다.
왜냐하면 관계의 주인이 book쪽의 아무런 변화가 일어나지않았기 떄문이다.
그래서 book쪽의 관계의 변경을 해줘야
book.setBookStore(this); 이렇게 !!
book에 이 관계의 주인의 book쪽에다가 bookStore를
설정을 해줘야하지만 db에 반영이 되는 것이다.

정답코드

Entity01 BookStore

@Entity
@Getter @Setter 
public calss BookStore {
			@Id @GenerateValue
	private Integer id; 
	
	private String name; 

	@OneToMany(mappedBy ="bookStore")
	private Set<book> books = new HashSet<>();
			//add 메세지로 책을 책방에 추가한 것 
			public void add(Book book){
        **book.setBookStore(this); //owner**
				this.books.add(book); //그러면 이건 왜?? 
			}
}

this.books.add(book); //그러면 이건 왜??
이런 질문은 왜 JPA를 쓰냐 ORM을 쓰냐하는 질문하고 똑같은 것
우리는 객체지향적이 코드라 데이터 베이스관계형 DB를 다루기 때문에
JPA를 쓰는 것이다.
객체지향적인 시각에서 바라봐도 당연히 이 관계를 맺을 때는
양방향 관계에서 둘다 설정이 되는 것이 맞다.

Entity02 Book

@Entity
@Getter @Setter 

public calss Book {
		@Id @GenerateValue
		private Integer id; 

		private String isbn; 

		private String title; 

		@ManytoOne
		private BookStore;

}

testCode

@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoJpaTestApplicationTests{
		@Autowired
		BookStoreRepository bookStoreRepository; 

		@Autowired
		BookRepository bookRepository; 

    @Test 
		public void contexLoads(){
				//책방 보이죠?? 책방을 하나 만들고 
				bookStore bookStore = new BookStore();
				bookStore.setName("소피 책방");
				bookStoreRepository.save(bookStore);
				
				//책을 만들었다.
				Book book = new Book(); 
				book.setTitle("JPA공부 좀 하면서 쓰자"); 

				// 그 다음에 이 책을 책방에다가 추가했어요. 
				bookStore.add(book); 

				bookRepository.save(book);
	}

}