Notice
Recent Posts
Recent Comments
Link
관리 메뉴

look-forest

확장 기능 본문

JPA/Spring data JPA

확장 기능

studyHub 2024. 10. 20. 00:45

 

자동 생성되는 쿼리 대신, 쿼리를 직접 구현하고 싶은 경우엔 어떻게 할까?

  • 간단한 쿼리는 @Query와 JPQL로 정의.
  • 복잡한 SQL이 필요한 경우 네이티브 쿼리 사용.
  • 동적 쿼리에는 QueryDSL이 유용.
  • 고급 로직이나 복잡한 비즈니스 요구사항은 Custom Repository로 처리.

사용자 정의 리포지토리 구현

스프링 데이터 JPA 리포지토리는 인터페이스만 정의하고 구현체는 스프링이 자동 생성한다.

그런데 스프링 데이터 JPA가 제공하는 인터페이스를 직접 구현하면 구현해야 할 기능이 너무 많다.

실무에서는 주로 QueryDSL이나 SpringJdbcTemplate을 함께 사용할 때 사용자 정의 리포지토리 기능 자주 사용한다.

 

Spring data JPA 인터페이스의 모든 메소드가 아니라 특정 메서드만 직접 구현하고 싶다면 다음과 같이 진행하면 된다.

=> JpaRepository와 커스텀Repository(구현체 등록 필)를 둘 다 상속 받는다.

즉, 특정 메소드만 따로 인터페이스를 정의해서 구현하고, 그 인터페이스를 확장하면 된다.

 

1. 사용자 정의 인터페이스 생성 (MemberRepositoryCustom)

public interface MemberRepositoryCustom {
    List<Member> findMemberCustom();
}

2. 사용자 정의 인터페이스 구현 클래스 생성 (MemberRepositoryCustomImpl)
    - 규칙: [ 사용자 정의 인터페이스 명 + Impl ]  or [ 리포지토리 인터페이스 이름 + Impl ] 

public class MemberRepositoryCustomImpl implements MemberRepositoryCustom {

    private final EntityManager em;

    @Override
    public List<Member> findMemberCustom() {
        return em.createQuery("select m from Member m", Member.class)
                .getResultList();
    }
}

 

3. 사용자 정의 인터페이스 상속
스프링 데이터 JPA가 MemberRepositoryCustom Impl 을 인식해서 그 클래스의 구현 메소드도 사용 가능하게 된다.

public interface MemberRepository extends JpaRepository<Member, Long>, MemberRepositoryCustom{
}

 

★참고

항상 사용자 정의 리포지토리가 필요한 것은 아니다. 그냥 임의의 리포지토리를 만들어도 된다.
가령 MemberQueryRepository를 인터페이스가 아닌 클래스로 만들고 스프링 빈으로 등록해서 그냥 같이 사용해도 된다.

물론 이 경우 스프링 데이터 JPA와는 아무런 관계 없이 별도로 동작한다.

커스텀 리포지토리까지 만들어서 한 리포지토리에 모두 때려넣는 것이 옳다고만은 볼 수 없다.

중요 비즈니스 로직과 화면/API 를 위한 복잡한 로직을 구분하는 것도 가독성에도 좋고, 그 둘은 변경 주기가 다르기 때문에 명확히 분리하는 것이 깔끔하다.

 


Auditing(감사)

엔티티를 생성, 변경할 때 변경한 사람과 시간을 추적하고 싶으면?

JPA에서 @MappedSupperClass와 @PrePersist, @PreUpdate를 사용할 수 있지만,

스프링 데이터 JPA는 좀 더 직관적이고 간단하게 제공한다. (@CreatedBy, @LastModifiedDate 등)

 

설정

  • @EnableJpaAuditing : 자동 타임스탬프, 사용자 추적 기록
  • AuditorAware : 감사 정보를 자동으로 관리하기 위한 인터페이스
  • @EntityListeners(AuditingEntityListener.class) : 엔티티 생명 주기 이벤트에 대한 콜백을 처리하는 리스너 지정

등록자, 수정자를 처리해주는 AuditorAware 스프링 빈 등록 필요 (세션 정보나, 스프링 시큐리티 로그인 정보에서 ID를 받음, 위 예제에서는 랜덤값)
실무에서 대부분의 엔티티는 등록시간, 수정시간이 필요하지만, 등록자, 수정자는 없을 수도 있어서 분리
등록자, 수정자를 처리해주는 AuditorAware 스프링 빈 등록 필요

 

※ 참고
저장 시점에 등록일, 등록자는 물론이고, 수정일, 수정자도 같은 데이터가 저장된다. 데이터가 중복 저장되는 것 같지만,
이렇게 해두면 변경 컬럼만 확인해도 마지막에 업데이트한 유저를 확인할 수 있으므로 유지보수 관점에서 편리하다.
이렇게 하지 않으면 변경 컬럼이 null 일때 등록 컬럼을 또 찾아야 한다.

WEB 확장

도메인 클래스 컨버터

HTTP 파라미터로 넘어온 Id 엔티티 객체를 찾아서 자동으로 바인딩해준다!

도메인 클래스 컨버터가 중간에 동작해서 레파지토리를 통해 id로 회원 엔티티 객체를 조회 후 반환

 

# 주의할 점

도메인 클래스 컨버터로 엔티티를 파라미터로 받으면, 이 엔티티는 단순 조회용으로만 사용해야 한다.

트랜잭션이 없는 범위에서 엔티티를 조회했으므로, 엔티티를 변경해도 DB에 반영되지 않는다.


페이징과 정렬

스프링 데이터가 제공하는 페이징과 정렬 기능을 스프링 MVC에서 편리하게 사용할 수 있다.

단순히 Page, Pageable 인터페이스를 파라미터하면 된다.

 /members?page=0&size=3&sort=id,desc&sort=username,desc

위와 같이 요청을 보내면, 스프링 MVC가 자동으로 PageRequest 객체 생성해서 파라미터로 전달한다.

파라미터를 Pageable로 설정하면, MVC가 구현체인 PageRequest를 만들어 전달!

 

페이지 사이즈 

기본 20인데, 변경하고 싶다면 아래와 같이 설정하면 된다

글로벌 설정 : default-page-size 사용
개별 설정 : @PageableDefault 어노테이션을 사용

 

페이징 정보가 둘 이상

@Qualifier 에 접두사명 추가하면 요청 파라미터의 접두사로 구분이 가능하다. 

요청 예시 : /members?member_page=0&order_page=1

 

Page 내용을 DTO로 변환하기

엔티티를 API로 노출하면 다양한 문제가 발생한다. 그래서 엔티티를 꼭 DTO로 변환해서 반환해야 한다.

Page는 map() 을 지원해서 내부 데이터를 다른 것으로 변경할 수 있다.

Page<MemberDto> pageDto = page.map(MemberDto::new);

 

 

Page를 1부터 시작하기

  1. Pageable, Page를 파라미터와 응답 값으로 사용히지 않고, 직접  클래스를 만들어서 처리하는 방법
  2. spring.data.web.pageable.one-indexed-parameters 를 true 로 설정하는 방법
    (이 방법은 web에서 page 파라미터를 -1 처리 할 뿐이라, 페이지 관련 메타 정보는 0부터 시작해서 안 맞으니 주의)

참고 자료 & 이미지 출처
실전! 스프링 데이터 JPA (김영한 님)

 

'JPA > Spring data JPA' 카테고리의 다른 글

스프링 데이터 JPA 분석  (0) 2024.10.20
다양한 편의 기능  (0) 2024.10.19
공통 인터페이스 기능  (0) 2024.10.19