백엔드 개발자
[#1] 스프링 데이터 JPA 본문
공통 인터페이스
- CRUD에 해당하는 find, delete 등 많이 쓰이는데 항상 같은 코드를 반복하게 된다.
- JpaRepository
- 이것을 우리가 사용할 repository 인터페이스에서 JpaRepository<클래스, 클래스의 pk 자료형> 을 상속받으면 스프링 데이터 JPA에서 구현 클래스를 만들어 인터페이스를 구현한 프록시 객체를 만들어 준다.
- 우리는 인터페이스를 직접 구현하지 않고 find, findAll 등 지원하는 다양한 메서드를 사용할 수 있다.
메서드 쿼리
- 공통 인터페이스 말고 우리 도메인에 특화된 쿼리문도 필요할 것이다.
- 이때 정해진 규칙에 맞춰 메서드 이름을 작성하면 우리 도메인에 해당하는 쿼리로 구현체를 만들어준다.
- findByName 을 하면 name으로 찾아오는 쿼리를 자동 구현해 줄 것이다.
- And 로 여러개의 조건을 추가할 수도 있고, GreaterThan으로 값을 비교할 때 ≥(이상) 을 나타낼 수 있다.
- findById 같은 메서드에서 find 와 By 사이에 메서드 설명문을 아무거나 넣을 수도 있다.
- 엔터티에 없는 속성을 찾아오는 메서드를 작성하면 애플리케이션 실행시점에 오류를 잡아준다.
- 조건이 많아질수록 메서드 이름이 계속 길어진다.
- https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.query-creation
결과값을 Dto로 받아오기
JPQL 쿼리문으로 Dto로 받아오려면 select에서 Dto를 받을 수 있는데, 패키지 경로까지 다 써서
new로 객체를 새로 생성해주어야한다.
ex ) select new {{패키지경로}}.myDto(m.id, m.name) from Member m
파라미터 바인딩
- JPQL에서 파라미터를 :name 형식으로 넣어주는데 in :names 로 파라미터에 컬렉션을 넣어줄 수 있다.
제공하는 메서드 외에 추가 메서드를 생성하려면?
- 메서드 위에 바로 @Query 어노테이션으로 jpql을 작성할 수 있다.
- 혹은 우리가 인터페이스를 직접 구현할 수 있다.
- 그런데 한 가지 메서드를 추가하기 위해 인터페이스를 직접 구현하려면 implement 하면 jpa가 자동으로 구현체를 생성해주었던 메서드들도 전부 구현해 주어야 한다.
- 그 대신 새로운 인터페이스를 만들고 추가할 메서드들을 정의하여 직접 구현하고 상속받으면 기존 인터페이스는 jpa가 자동으로 구현해주고 내가 원하는 메서드만 추가로 정의하여 사용할 수 있다.
- 이 경우 구현체는 인터페이스이름 + Impl을 적용하는 것이 규칙이다.
- 인터페이스와 분리하여 아예 새로운 class를 만들어 사용할 수도 있다.
- 한 인터페이스로 모두 사용하려면 너무 커지고 복잡해 질 수 있다.
- 비지니스 로직에 따라 기능들을 분리하여 상황에 따라 새로운 클래스를 만들어 사용할 수 있다.
페이징
기존 메서드에서 매개변수로 Pagable 하나만 추가해 주면 된다.
- 요청은 PageRequest로 만들어 전달해준다.
Pagable은 시작 페이지, 사이즈, 정렬(선택) 로 구성할 수 있다.
반환 타입을 Page, Slice 로 나눌 수 있는데
- Page 는 content와 totalCount등을 반환해준다.
- Slice는 요청한 사이즈 보다 +1하여 데이터가 더 있는지 확인해준다.
- 데이터가 더 있을 때 더보기 버튼같은 것을 보여주면 Slice를 이용해 구현할 수 있다.
반환 타입에 따라 count 쿼리를 날리거나 하는 변화가 있는데 아예 List로 반환할 수도 있다
- 페이징 역할을 하는 것이기 때문에 limit관련해서만 페이징 해주게 된다.
total 카운트 쿼리는 모든 데이터를 조회해야하기 때문에 성능에 문제가 될 수 있다.
조인이 일어나는 쿼리에서 카운트 쿼리도 조인을 하여 count문을 실행하게 되는데 조인이 필요없는
상황에서 성능만 느려질 수 있다.
이때 일반 쿼리와 카운트 쿼리를 분리하여 사용할 수 있다.
@Query 어노테이션으로 jpql문을 작성할 때 countQuery 옵션에 따로 쿼리문을 작성해 준다.
Page<엔티티>로 받아온 값 Page
로 변환하여 매핑하기 - Page
dtos = page.map(m → new MemberDto());
- Page
벌크성 쿼리
여러 객체에 대해 한 번에 모두 업데이트 하는 쿼리
기존 jpa는 객체를 가져와서 수정을 해주면 영속성컨테이너에서 업데이트 되는 방식이다.
한 번에 여러 객체에 대해 업데이트를 하기 위해 바로 데이터베이스에 업데이트 시킨다.
기존 sql을 사용해서 update를 진행하는 것과 똑같은데 jpa에서는 객체 중심으로 하나씩 가져와서
변경해주었기 때문에 여기서는 바로 데이터베이스에 업데이트 해준다는 것이 다르다.
영속성 컨텍스트를 안거치고 바로 데이터베이스에 업데이트 되기 때문에 영속성 컨텍스트에는 반영이 안된 것에 주의해주어야 한다.
select문과 달리 update문을 사용하여 executeUpdate를 실행해주기 위해 @Modifying을 추가해 주어야 한다.
- @Modifying(clearAutomatically = true) 옵션을 추가해 주면 벌크업 쿼리를 실행한 후 영속성 컨텍스트를 clear해주기 때문에 위에 언급한 동기화 문제를 해결할 수 있다.
EntityGraph
- 기존 fetch 조인을 쿼리문으로 직접 작성하던걸 어노테이션으로 지원하는 기능이다.
- 메서드 쿼리에서도 fetch 조인을 쓰기 위해 쿼리문을 써줘야 했는데 쿼리문 대신
- @EntityGraph(attributePaths = {”fetch조인 대상”})을 쓰면 fetch 조인한 결과를 얻을 수 있다.
- fetch조인 대상에는 반환 객체에 포함된 fetch 조인 할 연관관계 대상의 이름을 쓰면 된다.
@QueryHints
- (value = @QueryHint(name = “org.hibernate.readOnly”, value = “true”))
- 변경할 목적 없이 그냥 조회만 하고 싶을 때 사용할 수 있다.
- 성능 최적화 목적이다.
- jpa는 변경감지를 위해 조회를 하면 영속성 컨텍스트에 원본과 복사본까지 가져오게 되는데 변경할 일이 없는 경우에는 이 어노테이션을 사용해서 성능 최적화를 할 수 있다.
'스프링 > 스프링 데이터 JPA' 카테고리의 다른 글
[#2] 스프링 데이터 JPA (0) | 2023.09.10 |
---|