Spring Data JPA가 아닌 다른 이유로 인터페이스의 메서드를 직접 구현하고 싶을 때 사용하면 된다. 예를들어, JPA 직접 사용, 스프링 JDBC Template사용, MyBatis사용, Querydsl사용 등등이 있다.

 

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


먼저, 인터페이스를 만들어야 한다.
MemberRepositoryCustom Interface 생성

package study.datajpa.repository;

import study.datajpa.entity.Member;
import java.util.*;

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

그리고 인터페이스를 구현할 구현체 클래스를 만들어준다.

MemberRepositoryCustomImpl class 생성

package study.datajpa.repository;

import lombok.RequiredArgsConstructor;
import study.datajpa.entity.Member;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.List;

@RequiredArgsConstructor
public class MemberRepositoryCustomImpl implements MemberRepositoryCustom{
    private final EntityManager em;

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

그리고 Spring Data JPA 클래스에 인터페이스를 상속하면 된다.

MemberRepository 코드 추가 (인터페이스 상속)

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

이 기능은 Java에서 제공해주는 것이 아닌, Spring Data JPA에서 제공해주는 기능이다. 특히, 김영한님은 복잡해지는 쿼리들을 querydsl을 쓰기 위해 커스텀해서 사용한다고 한다.

 

 

해당 기능을 사용할 때 규칙이 있다.

  • 규칙 : 리포지토리 인터페이스 이름 + Impl
  • 규칙을 지켜야 Spring Data JPA가 인식해서 스프링 빈으로 등록한다고 한다.
  • "리포지토리 인터페이스 이름" + "아무값" + "Impl" 적어도 인식하여 등록해주는 것 같다.
변경하는 방법도 있는데, 왜만하면 관례를 따르는게 유지보수성면에서 좋아보인다.

 

 

 

Auditing


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

  • 등록일
  • 수정일
  • 등록자
  • 수정자

기본적으로 테이블을 만들 때, 등록일/수정일을 만들어야 운영에서 지옥을 맛보지 않는다고 한다. 추적이 되지 않기 때문이라고한다. 언제 등록이 됐고, 언제 수정이 됐는지는 기본으로 깔고가는 습관을 들여보자!

 

순수 JPA 사용

JpaBaseEntity 클래스 생성

package study.datajpa.entity;

import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import java.time.LocalDateTime;

@MappedSuperclass
public class JpaBaseEntity {

    @Column(updatable = false)
    private LocalDateTime createdDate;
    private LocalDateTime updatedDate;

    @PrePersist     // persist하기전에 event
    public void prePersist() {
        LocalDateTime now = LocalDateTime.now();
        createdDate = now;
        updatedDate = now;
    }

    @PreUpdate      // update하기전에 event
    public void preUpdate() {
        updatedDate = LocalDateTime.now();
    }
}

Member 엔티티에 상속

public class Member extends JpaBaseEntity {
	...
}

Table 생성 쿼리

 

 

Spring Data JPA 사용

Main 클래스에 @EnableJpaAuditing, 등록자/수정자 메소드 추가

@EnableJpaAuditing
@SpringBootApplication
public class DataJpaApplication {
	...
    
    // 등록자, 수정자 값 가져오는 메소드
	@Bean
	public AuditorAware<String> auditorProvider() {
		return () -> Optional.of(UUID.randomUUID().toString());
	}
}

BaseEntity 클래스 생성

package study.datajpa.entity;

import lombok.Getter;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import java.time.LocalDateTime;

@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
@Getter
public class BaseEntity {

    // 등록일
    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdDate;

    // 수정일
    @LastModifiedDate
    private LocalDateTime lastModifiedDate;

    // 등록자
    @CreatedBy
    @Column(updatable = false)
    private String createdBy;

    // 수정자
    @LastModifiedBy
    private String lastModifiedBy;
}

 

 

 

Web 확장 - 페이징과 정렬


 

Web에서 페이징과 정렬을 하는 방법이다.

 

MemberController 메소드 추가

public class MemberController {
    ...
    @GetMapping("/members")
    public Page<Member> list(Pageable pageable){
        Page<Member> page = memberRepository.findAll(pageable);
        return page;
    }

    @PostConstruct
    public void init() {
        for(int i = 0; i < 100; i++){
            memberRepository.save(new Member("user"+i, i));
        }
    }
}

 

파라미터에 Pageable을 추가하면 페이징과 sort기능을 사용할 수 있다. Page<Member>로 반환값을 하면 토탈값을 알 수 있다.

위처럼 url+"?page={페이지번호}&size={원하는사이즈}&sort={sort하고싶은 컬럼명},desc(기본 asc)"
을 사용하면 페이징과 정렬이 가능하다. 

 

기본이 page별 사이즈가 20인데 apllication.yml, 파라미터로 수정할 수 있다.

application.yml

  data:
    web:
      pageable:
        default-page-size: 10
        max-page-size: 2000

파라미터

    @GetMapping("/members")
    public Page<Member> list(@PageableDefault(size = 5) Pageable pageable){
        Page<Member> page = memberRepository.findAll(pageable);
        return page;
    }

 

 

접두사

페이징 정보가 둘 이상이면 접두사로 구분한다.
  • @Qualifier 에 접두사명 추가 "(접두사명)_xxx"
  • 예) /members?member_page=0&order_page=1 
  public String list(
      @Qualifier("member") Pageable memberPageable,
      @Qualifier("order") Pageable orderPageable, ...
  }

 

 

Dto 반환

    @GetMapping("/members")
    public Page<MemberDto> list(@PageableDefault(size = 5) Pageable pageable){
        return memberRepository.findAll(pageable)
                .map(member -> new MemberDto(member));
    }

위와 같이 page에서 제공해주는 기능을 이용하면 된다.

 

 

 

 

 

 

 

출처

 

실전! 스프링 데이터 JPA - 인프런 | 강의

스프링 데이터 JPA는 기존의 한계를 넘어 마치 마법처럼 리포지토리에 구현 클래스 없이 인터페이스만으로 개발을 완료할 수 있습니다. 그리고 반복 개발해온 기본 CRUD 기능도 모두 제공합니다.

www.inflearn.com

 

+ Recent posts