출처 : 실전! 스프링 데이터 JPA
내가 보려고 씀
섹션 4. 쿼리 메소드 기능
8. 순수 JPA 페이징과 정렬
JPA에서 페이징을 어떻게 할 것인가?
9. 스프링 데이터 JPA 페이징과 정렬
페이지를 유지하면서 엔티티를 DTO로 변환하기
→ 엔티티를 외부에 그대로 반환하면 안 됨.
10. 벌크성 수정 쿼리
JPA는 엔티티 객체 중심
11. @EntityGraph
※ 선행 : 페치조인이란?
→ 지연로딩과 그로인해 발생하는 문제들을 이해해야 함.
→ N + 1 문제 : 페치조인으로 해결
// 영속성 컨텍스트에 있는 캐시 정보들을 DB에 완전히 다 반영을 해서
// insert를 정확하게 다 하고,
// 데이터베이스에 다 반영을 시킨 다음에
// 영속성 컨텍스트를 다 날리는 것
em.flush();
em.clear();
* [Member.java]
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "team_id")
private Team team;
→ 지연로딩을 하면 Member 쿼리를 가져올 때 Team은 가짜객체(프록시)를 가져온다.
* [Member.java]
// fetch : Member를 조회할 때 연관된 팀을 한방 쿼리로 다 끌고온다.
@Query("select m from Member m left join fetch m.team")
List<Member> findMemberFetchJoin();
* fetch join을 하지 않을 시 team class
member.teamClass = class study.datajpa.entity.Team$HibernateProxy$x50Fg8Vr
* fetch join을 하지 않을 시 쿼리 : 총 3번 나감
select
member0_.member_id as member_i1_0_,
member0_.age as age2_0_,
member0_.team_id as team_id4_0_,
member0_.username as username3_0_
from
member member0_
select
team0_.team_id as team_id1_1_0_,
team0_.name as name2_1_0_
from
team team0_
where
team0_.team_id=?
select
team0_.team_id as team_id1_1_0_,
team0_.name as name2_1_0_
from
team team0_
where
team0_.team_id=?
* fetch join 시 team class
member.teamClass = class study.datajpa.entity.Team
* fetch join 시 쿼리 : 총 1번 나감
select
member0_.member_id as member_i1_0_0_,
team1_.team_id as team_id1_1_1_,
member0_.age as age2_0_0_,
member0_.team_id as team_id4_0_0_,
member0_.username as username3_0_0_,
team1_.name as name2_1_1_
from
member member0_
left outer join
team team1_
on member0_.team_id=team1_.team_id
EntityGraph 정리
- 사실상 페치 조인(FETCH JOIN)의 간편 버전
- LEFT OUTER JOIN 사용
12. JPA Hint & Lock
JPA Hint
JPA 쿼리 힌트 (SQL 힌트가 아니라 JPA 구현체(하이버네이트)에게 제공하는 힌트)
섹션 5. 확장 기능
1. 사용자 정의 리포지토리 구현
Querydsl 사용할 때 많이 씀
사용자 정의 구현 클래스
- 규칙: 리포지토리 인터페이스 이름 + Impl
- 스프링 데이터 JPA가 인식해서 스프링 빈으로 등록
2. Auditing
엔티티를 생성, 변경할 때 변경한 사람과 시간을 추적하고 싶으면?
예를 들어,
- 등록일
- 수정일
- 등록자
- 수정자
3. Web 확장 - 도메인 클래스 컨버터
HTTP 파라미터로 넘어온 엔티티의 아이디로 엔티티 객체를 찾아서 바인딩
권장하진 않음.
4. Web 확장 - 페이징과 정렬
스프링 데이터가 제공하는 페이징과 정렬 기능을 스프링 MVC에서 편리하게 사용할 수 있다.
🚨 엔티티를 외부에 노출하는 것은 내부 설계를 밖에 노출하는 것과 같다.
규약이 의미가 없음.
→ API를 반환할 때는 DTO로 반환하는 게 좋다.
(반환할 것만...)
💡 엔티티는 DTO를 가급적 보지 않는 게 좋다.
같은 패키지 안에 있는 경우 제외.
DTO라는 것은 공통으로 다 보는 것.
따라서 DTO는 엔티티를 봐도 된다.
섹션 6. 스프링 데이터 JPA 분석
1. 스프링 데이터 JPA 구현체 분석
- @Repository 적용: JPA 예외를 스프링이 추상화한 예외로 변환
- @Transactional 트랜잭션 적용
- JPA의 모든 변경은 트랜잭션 안에서 동작
- 스프링 데이터 JPA는 변경(등록, 수정, 삭제) 메서드를 트랜잭션 처리 (아니면 예외 터짐)
- 서비스 계층에서 트랜잭션을 시작하지 않으면 리파지토리에서 트랜잭션 시작
- 서비스 계층에서 트랜잭션을 시작하면 리파지토리는 해당 트랜잭션을 전파 받아서 사용
- 그래서 스프링 데이터 JPA를 사용할 때 트랜잭션이 없어도 데이터 등록, 변경이 가능했음 (사실은 트랜잭션이 리포지토리 계층에 걸려있는 것임)
- @Transactional(readOnly = true)
- 데이터를 단순히 조회만 하고 변경하지 않는 트랜잭션에서 readOnly = true 옵션을 사용하면,
플러시를 생략해서 약간의 성능 향상을 얻을 수 있음
- 데이터를 단순히 조회만 하고 변경하지 않는 트랜잭션에서 readOnly = true 옵션을 사용하면,
트랜잭션이 끝날 때 플러시를 하고, 커밋을 한다.
readOnly = true가 있으면 플러시를 안 함.
💡 플러시를 안 함 : DB에 변경감지가 안 일어나고, DB에 데이터를 안 보내겠다는 것
(∵ 읽기전용이기 때문)
⭐ 매우 중요!!! ⭐
save() 메서드
- 새로운 엔티티면 저장( persist ) (en.persist())
- 새로운 엔티티가 아니면 병합( merge ) (em.merge()) : 기존에 한번 DB에 들어갔다가 나온 애야 라는 뜻
- DB에 가져온 데이터를 바꿔치기 한다.
- merge를 호출하는 순간, DB에서 꺼내고 파라미터를 넘긴 애로 다 교체를 한다. 트랜잭션이 끝날 때 데이터가 바꼈기 때문에 DB에 반영이 되는 메커니즘으로 동작함
- 웬만하면 쓰면 안 된다. 데이터를 업데이트 하고 싶으면, 변경감지를 써야 함.
2. 새로운 엔티티를 구별하는 방법
새로운 엔티티를 판단하는 기본 전략
- 식별자가 객체일 때 null 로 판단
- 식별자가 자바 기본 타입일 때 0 으로 판단
※ 참고
@NoArgsConstructor(access = AccessLevel.PROTECTED)
// protected 기본 생성자 대신
protected Member() {}
섹션 7. 나머지 기능들
실무에서 잘 사용하진 않음.
편하게 들어도 된다.실무에서 쓰기 애매한 기능들.
JPA Criteria : 실무에서 안 씀
→ 실무에서는 JPA Criteria를 거의 안쓴다! 대신에 QueryDSL을 사용하자.
'Spring > JPA' 카테고리의 다른 글
[JPA] Querydsl 찍먹해보기 (0) | 2023.02.06 |
---|---|
[JPA] MyBatis와 JPA, 도대체 뭐가 다를까? (0) | 2023.02.06 |
스프링 데이터 JPA - (1) (0) | 2022.12.19 |
JPA 활용 2 - API 개발과 성능 최적화 (0) | 2022.12.19 |
JPA 강의 8 - 객체지향 쿼리 언어 2, JPA N + 1 문제 (중급 문법) (0) | 2022.12.19 |