[Spring] 스프링 데이터 JPA 확장 기능 및 기타 기능
확장 기능
사용자 정의 리포지토리 구현
지금까지 다룬 스프링 데이터 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을 적고 nativeQuery
를 true
로 설정해주면 된다.
하지만 네이티브 쿼리는 한계가 많아서 가급적이면 사용하지 않는게 좋다!
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
댓글남기기