2 분 소요

확장 기능

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

지금까지 다룬 스프링 데이터 JPA 리포지토리는 interface만 정의하고 구현체는 스프링에서 생성해줬다. 이 interface
에 있는 일부 함수를 구현하고 싶다면 구현할 수 있으나 interface 내 모든 함수를 같이 구현해줘야 하는 문제점이 있
다. 이 때 원하는 함수만 구현할 수 있는 방법이 있는데 아래 예시를 참고하자.

findMemberCustom()라는 함수를 구현하고 싶고, 기존에 있던 모든 함수는 MemberRepository interface에 정의돼
있다고 하자. 그러면 구현하려는 함수를 MemberRepository가 아니라 새로운 interface MemberRepositoryCustom
에 정의해야 한다. (interface 이름 제약없음)

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


그리고 MemberRepositoryImpl라는 클래스를 만들고 방금 만든 MemberRepositoryCustom를 구현해주자.

@RequiredArgsConstructor
public class MemberRepositoryImpl implements MemberRepositoryCustom {

    private final EntityManager em;

    @Override
    public List<Member> findMemberCustom() {
        return em.createQuery("select m from Member m", Member.class)
                .getResultList();
    }
}
  • 클래스 명은 반드시 MemberRepositoryImpl 또는 MemberRepositoryCustomImpl이어야 함

그리고 MemberRepository에선 MemberRepositoryCustom을 상속받으면 된다.

public interface MemberRepository extends JpaRepository<Member, Long>, MemberRepositoryCustom {
  //...
}
  • MemberRepository.findMemberCustom()로 사용할 수 있음

Auditing

여러 엔티티에 대해서 공통 필드를 적용하고 싶은 상황이 있다. 예를 들면 테이블마다 등록일, 수정일 등을 확인하고
실을 때 활용할 수 있다.

일단 main()이 있는 스프링 부트 클래스에 @EnableJpaAuditing을 붙여주자.

@EnableJpaAuditing
@SpringBootApplication
public class DataJpaApplication {
	public static void main(String[] args) {...}

  @Bean
	public AuditorAware<String> auditorProvider() {
		return () -> Optional.of(UUID.randomUUID().toString());
	}
}
  • 등록자, 수정자를 처리해줄 AuditorAware를 스프링 빈으로 등록
    • 예시로 UUID를 생성해서 설정해주고 있음
    • 실무에서는 세션 정보 or ID(from 스프링 시큐리티 로그인 정보)로 설정

등록일, 수정일, 등록자, 수정자 필드가 있는 클래스를 정의하자.
반드시 @EntityListeners(AuditingEntityListener.class)를 적용해야 한다!

@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
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;
}
  • @MappedSuperclass : BaseEntity를 상속받는 클래스는 BaseEntity의 필드 정보를 가질 수 있음
  • 필드에 @CreatedDate, @LastModifiedDate, @CreatedBy, @LastModifiedBy 사용
  • @Column(updatable = false) : 업데이트 쿼리 무시

그리고 Member 엔티티에 BaseEntity를 적용하고 싶으면 다음처럼 설정해주면 된다.


public class Member extends BaseEntity {
  //...
}


페이징과 정렬

controller에서 Page를 리턴 타입으로 설정할 수 있다.

@GetMapping("/members")
public Page<Member> list(@PageableDefault(size = 5) Pageable pageable) {
    return memberRepository.findAll(pageable);
}
  • 파라미터로 Pageable를 설정함에 주의
  • @PageableDefault
    • size : 각 page에 담을 데이터를 5건으로 설정
    • sort : 정렬 조건으로 asc(기본), desc 사용 가능
    • 해당 파라미터에만 적용하는 기준이고, 아래는 스프링 부트에서 글로벌로 설정하는 코드
      • spring.data.web.pageable.default-page-size : 기본 page 사이즈
      • spring.data.web.pageable.max-page-size : 최대 page 사이즈

기타 기능

Projections

엔티티 전체 정보가 아닌 일부 정보만 조회하고 싶을 때 Projections를 사용할 수 있다.
예를 들어 이름만 조회하고 싶을 때 다음처럼 UsernameOnly라는 interface를 정의해주자.

public interface UsernameOnly {
  String getUsername();
}


그러면 MemberRepository에서 함수의 리턴 타입으로 UsernameOnly를 적어서 처리할 수 있다.

public interface MemberRepository extends JpaRepository<Member, Long> {
  List<UsernameOnly> findProjectionsByUsername(String username);
  <T> List<T> findProjectionsByUsernameWithGeneric(String username, Class<T> type);
}
  • Generic type 설정 가능
    • 사용 시 type 자리에 UsernameOnly.class를 적어주면 됨
  • 일부 필드만 가져오도록 쿼리가 날아가기 때문에 최적화가 된다는 장점이 있음
  • 하지만 다른 엔티티와 join 시 연관된 엔티티에 대해서는 최적화가 되지 않는 한계 존재

네이티브 쿼리

네이티브 쿼리를 쓰고싶다면 value에 SQL을 적고 nativeQuerytrue로 설정해주면 된다.
하지만 네이티브 쿼리는 한계가 많아서 가급적이면 사용하지 않는게 좋다!

public interface MemberRepository extends JpaRepository<Member, Long> {
  @Query(value = "select * from member where username = ?", nativeQuery = true)
  Member findByNativeQuery(String username);
}


projections를 적용할 수도 있다.

@Query(value = "select m.member_id as id, m.username, t.name as teamName" +
               "from member m left join team t",
               countQuery = "select count(*) from member",
               nativeQuery = true)
Page<MemberProjection> findByNativeProjection(Pageable pageable);
  • countQuery : Page로 반환하기 때문에 count 쿼리를 따로 설정
  • 리턴 타입이 MemberProjection

카테고리:

업데이트:

댓글남기기