본문 바로가기

Back-End/JPA

Spring JPA의 사실과 오해 - NHN FORWARD

1. 연관관계 매핑

Entity 매핑 / 연관관계 매핑

정규화를 통해서 최소한의 데이터를 받아오도록 한다. 그렇기 때문에 FK를 이용하여 객체 참조와 매핑항여 사용한다.

 

그때 다중성, 방향성을 고려하여 설정을 한다. ( OneToOne, OneToMany, ManyToOne, ManyToMany )

 

대개의 경우 단방향 매핑이면 충분하다. ( 양방향 매핑은 복잡하고 객체에서 양쪽 방향을 모두 관리해줘야 함 )

 

영속성 전이

 

다대일인 경우 단방향으로도 영속성 전이가 잘됨

 

일대다 단방향 연관관계 매핑인 경우(Cascade.ALL) :

@Transactional
public void creeateMember(){
  memberRepository.save(member);

  // MemberDetail 생성및 세팅

}

결과는 innsert member / insert member1,member2 / update 2번 이런식 이렇게 됨

 

즉, 일대다 단방향 연관관계 매핑에서 영속성 전이(cascade)를 통한 insert 시에는 추가 update 쿼리가 발생하기 때문에 양방향 연관관계로 설정하는 것이 더 좋다.

 

public class MemberDetail(
	@EmbeddedIdd
    private Pk pk;
    
    @ManyToOne
    @MapsId("memberId")	// 연관관계의 조인
    private Member member;
    
    @Embeddable
    public static class Pk {
		@Column(name="member_id")
        private Long memberId;
        
        private String Id;
	}
}

public class Member {

	@OneToMany(mappedBy ="member", cascaedd = CascaddeType.ALL)
    private List<MemberDetail> memberDetails;
}
일대다 insert 시에는 양방향이 좋다!

연관관계 매핑 - eager, lazy

N+1문제가 발생함. fetch join, entity graph로 해결한다.

 

N+1 문제는 EAGER fetch 전략 때문에 발생한다? ( x ) => LAZY 여도 entity를 참조하면 N+1 발생

  • Pagination + Fetch JOIN : 실제로는 모든 레코드를 가져오는 쿼리가 실행된다. ( 즉 메모리에는 결국 다 로딩됨 )
  • 둘 이상의 컬렉션을 FetchJoin - MultipleBagFetchException
    • List를 Set으로 변경 or @OrderColumn

2. Spring Data Jpa Repository

data access layer 구현을 위해 반복적으로 작성했던 유사한 코드를 줄이기 위한 추상화 제공

메서드 이름 규칙을 통한 쿼리 생성

 

JOIN 쿼리는 실행할 수 없다?

Page vs Slice

Page 쿼리 실행시 select count(*) 가 추가돼서 쿼리가 생성됨. 일부분만 가져오고 싶은 경우 성능상 Slice를 추천함

 

// select * .. offset {offset} limit {limit_plus_1} select count(*) from ...

public interface Page<T> extendd Slice<T> {
	int getTotalPages ();
    
    }

DTO project은 JPA로 할수 없다?

  • class
@Value
public class MemberDTO {
	private final String name;
}

Collection<MemberDTO> findBy...();
  • interface
public interface MembeerNameOnly{
	String geetName()
    List<MemberDetailDTO> getDetails();
    
    interface MemberDetailDTO{
    	@Value("#{target.pk.type}")
    	Strinng getType();
        
        String getDDescription();
    }
}