public interface MemberRepository extends JpaRepository<Member, Long> {
}
Spring Data Jpa를 기본으로 사용할 때, Querydsl과 같은 기술을 이용해 복잡한 메서드를 추가하고 싶을 수 있다. 하지만 위의 코드를 보면 MemberRepository(Spring Data Jpa)는 인터페이스이기 때문에 메서드 로직 작성이 불가능하다. 직접 구현을 위해 다음과 같은 과정을 거친다.
Spring Data Jpa 인터페이스 이름 + Impl or 사용자 정의 인터페이스 이름 + Impl로 작성 -> Spring이 이를 인식하고 스프링 빈으로 등록(ex. MemberRepositoryImpl or MemberRepositoryCustomImpl)Querydsl + SpringDataJpa의 혼합형태를 많이 사용하기에 이 구조는 잘 알아둘 필요가 있다.
Spring Data Jpa 인터페이스 + Impl보다 사용자정의 인터페이스 이름 + Impl이 더 자바문법에 맞게 직관적이기 때문에 이를 이용하는 것이 모범적이라고 생각한다.
// 사용자 정의 인터페이스
public interface MemberRepositoryCustom {
List<Member> findMemberCustom();
}
// Impl 구현체
@RequiredArgsConstructor
public class MemberRepositoryImpl implements MemberRepositoryCustom {
private final EntityManager em;
querydsl...
@Override
public List<Member> findMemberCustom() {
querydsl...
} }
// 결합, 기존 MemberRepository 인터페이스에 사용자 정의 인터페이스 추가
public interface MemberRepository
extends JpaRepository<Member, Long>, MemberRepositoryCustom {
}
엔티티를 생성, 변경시 변경한 사람과 시간을 추적하기 위해 보통 가장 기본적으로 등록, 수정일을 추가적으로 등록자, 수정자까지 넣는 경우가 존재한다.
이 사항은 모든 엔티티에 적용될 수 있기에 이를 어노테이션을 통해 특별히 처리하여 간단히 활용할 수 있다.
등록, 수정일 필드는 필수. 등록자, 수정자는 선택사항으로 가정한다면 다음과 같이 BaseEntity 작성이 가능하다.(실무 스타일)
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
@Getter
public class BaseTimeEntity {
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdDate;
@LastModifiedDate
private LocalDateTime lastModifiedDate;
}
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
@Getter
public class BaseEntity extends BaseTimeEntity {
@CreatedBy
@Column(updatable = false)
private String createdBy;
@LastModifiedBy
private String lastModifiedBy;
}
이렇게 BaseEntity들을 설계한 후 원하는대로 엔티티에 상속받아 사용하면 된다.
@CreatedDate, @LastModifiedDate의 경우 생성, 변경 시점을 자동으로 인식하여 처리해준다.
@CreatedBy,@LastModifiedBy의 경우에는 AuditorAware를 등록해야하는데, 이때 Application 파일에 작성해주어야할 것이 있다.
@SpringBootApplication
@EnableJpaAuditing
public class FactoryApplication {
public static void main(String[] args) {
SpringApplication.run(FactoryApplication.class, args);
}
@Bean
public AuditorAware<String> auditorProvider() {
return new AuditorAwareImpl();
}
}
/// AuditorAwareImpl()
public class AuditorAwareImpl implements AuditorAware<String> {
@Override
public Optional<String> getCurrentAuditor() {
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attr != null) {
HttpSession session = attr.getRequest().getSession(false);
if (session != null) {
// 세션에서 loginID 추출
String loginId = (String) session.getAttribute(SessionConst.LOGIN_MEMBER);
return Optional.ofNullable(loginId);
}
}
return Optional.empty();
}
}
AuditorAware<String>을 구현하여 빈에 등록시키면, 이 구현체의 getCurrentAuditor()에 따라 @CreatedBy,@LastModifiedBy가 동작한다.
위의 코드는 세션 존재시 로그인 ID를 기준으로 작성했다. login된 상황에서 수정이나 생성할 경우 해당 필드들이 loginId 정보로 채워지는 것이다.
스프링 데이터가 제공하는 페이징과 정렬 기능을 스프링 MVC에서 편리하게 사용할 수 있다.
@GetMapping("/members")
public Page<Member> list(Pageable pageable) {
Page<Member> page = memberRepository.findAll(pageable);
return page;
}
위와 같이 Page 객체 자체를 곧바로 리턴타입으로 주고 json으로 보낼수도 있으며 스프링 데이터 jpa 자체에서 기본 기능에 pageable을 받을 수 있도록 표준화 해놓았다. findAll()을 그냥 사용할 경우 모든 데이터를 뽑아낼 것이지만, pageable과 같이 넘겨줄 경우 page처리가 된 데이터를 Page에 감싸 리턴할 수 있다.
Pageable을 어떻게 입력 파라미터로 받을 수 있는건데?
요청 파라미터의 정해진 표현을 충족하면 입력 파라미터 Pageable로 곧바로 받아낼 수 있는데 다음과 같다.
예) /members?page=0&size=3&sort=id,desc&sort=username,desc
sort 파라 미터 추가 ( asc 생략 가능)#yaml
spring:
data:
web:
pageable:
default-page-size: 20 # 기본 페이지 사이즈
max-page-size: 2000 # 최대 페이지 사이즈
public String list(@PageableDefault(size = 12, sort = "username",
direction = Sort.Direction.DESC) Pageable pageable) {
...
}