반응형

오늘의 공부는 백기선님의 @OneToMany 양방향 관계 쿼리 문제입니다.

원본 영상은 아래 링크를 참조해주세요~

 

 

@Entity
@Getter @Setter
public class Book {
	@Id @GeneratedValue
    private Integer id;
    
    private String isbn;
    
    private String Title;
    
    @ManyToOne
    private BookStore bookstore;
}

@Entity
@Getter @Setter
public class BookStore {
	@Id @GeneratedValue
    private Integer id;
    
    private String name;
    
    @OneToMany(mappedBy="bookStore")
    private Set<Book> books = new HashSet<>();
    
    void add(Book book) {
    	this.books.add(book);
    }
}

@Test
public void contextLoads() {
	BookStore bookStore = new BookStore();
    bookstore.setName("시애틀 책방");
    bookStoreRepository.save(bookStore);
    
    Book book = new Book();
    book.setTitle("JAP 공부 좀 하면서 쓰세요");
    
    bookStore.add(book);
    bookRepository.save(book);
}

책 테이블에 연관관계가 설정되지 않은 모습

Q . 왜 책 테이블에 연관관계가 설정되지 않았는가?

양방향 관계의 기본 문제이다. 테이블과 객체는 본질적으로 다르다.

테이블은 foreign key 가 어디 있던지 각 테이블에서 join 에서 조회할 수 있지만, 객체는 알 수 없다.

그래서 양방향 관계 설정 시 연관관계의 주인이 누구인지(FK 키를 누가 가지고 있는지) 알려주어야 한다.

 

mappedBy 속성으로 상대편 테이블이 주인이라는 것을 알려준다.

관계의 주인인 쪽에서 관계가 설정이 되어야 한다. 그래야 데이터베이스에 제대로 반영이 된다.

 

그래서 public void add(Book book) 메서드 내에 book.setBookStore(this); 를 삽입하면 문제 해결이 된다.

 

반대 코드인 getBooks().add(book) 도 객체 지향적인 관점에서 보았을 때 당연히 해주어야 한다.

데이터베이스에 적용할 때는 아무런 작용이 되지 않지만 객체에서도 값을 적용해주어야 하기 때문에 당연히 해야 한다.

반응형

'JAVA > JPA' 카테고리의 다른 글

[JPA][백기선] @ManyToOne 단방향 관계 쿼리 문제  (0) 2022.04.09
반응형

오늘의 공부는 백기선님의 @ManyToOne 단방향 관계 쿼리 문제입니다.

원본 영상은 아래 링크를 참조해주세요~

 

 

Q . 왜 테스트 코드에서는 select 쿼리가 한 개만 발생하고 Controller 코드에서는 3개가 발생하는가?

테스트 코드의 트랜잭션의 범위는 testSelect 함수이다.

@DataJpaTest 어노테이션이 @Transactional 어노테이션을 가지고 있는데 이는 public 메서드들에 트랜잭션 단위를 부여하는 것과 동일하다. 즉 testSelect 함수 전체가 하나의 트랜잭션이다.

트랜잭션 하나에 Persistence Context 를 가지고 있으므로 save 를 할 때마다 1차 cache 에 저장된다.

그러므로 1차 cache 에서 바로 꺼낼 수 있기 때문에 다시 select 할 필요가 없다.

 

그런데 Controller 코드에서는 findAll 하나만 트랜잭션 범위이다.

기본 전략이 EAGER 이므로 Team 정보도 같이 가져올 수 밖에 없다.

Q . Member 정보만 쿼리 한 번으로 가져오려면 어떻게 해야 하는가?

LAZY 라는 Fetch 옵션을 사용하는 방법을 생각해 볼 수 있다.

Fetch 전략은 처음 Member 라는 정보를 조회해올 때 Team 을 참조하는 코드가 있지 않는 이상 Team 에 대한 데이터를 조회해오지 않는다. Team 에 대한 정보를 조회하는 순간이 오면 그 때 Team 에 대한 select 쿼리가 발생한다.

 

그러나, findAll 메서드는 객체를 JSON 형태로 변환해서 전달해 주는데 프록시인 객체를 JSON 으로 변환하려고 하니까 오류가 발생한다. LAZY 옵션을 사용하면 Team 에 대한 레퍼런스가 ByteBuddyInterceptor 라는 프록시 객체로 변경되기 때문이다. DTO 를 사용해서 modelmapper 로 다시 맵핑하면 된다.

 

댓글 중에 interface 를 사용하는 방법이 있다고 한다. projection!

Q . Member 와 Team 정보를 쿼리 한 번으로 가져오려면 어떻게 해야 하는가?

JPQL 에서 fetch join 을 사용하면 간단하게 해결이 될 것 같은데... JPQL 을 사용하지 않은 것을 전제로 하는 것 같다.

 

(공부해보니...)

fetch join 은 다음과 같은 단점도 존재한다. 같은 테이블을 조회하는 경우지만 어떤 테이블을 같이 조회하느냐에 따라 중복되서 쿼리를 작성하기 때문에 비추천한다고 한다.

 

@NamedEntityGraph 를 사용해서 Join 쿼리를 유도한다.

엔티티 그래프 기능은 엔티티 조회 시점에 연관된 엔티티들을 함께 조회하는 기능이다.

@NamedEntityGraph(name="엔티티 그래프 이름", attributeNodes={@NamedAttributeNode("함께 조회할 속성")})

반응형

'JAVA > JPA' 카테고리의 다른 글

[JPA][백기선] @OneToMany 양방향 관계 쿼리 문제  (0) 2022.04.09

+ Recent posts