사용자 정의 리포지토리 구현
스프링 데이터 JPA 리포지토리는 인터페이스만 정의하고 구현체는 스프링이 자동 생성한다.
스프링 데이터 JPA가 제공하는 인터페이스를 직접 구현하면 구현해야 하는 기능이 너무 많다.
- MemberRepositoryCustom
public interface MemberRepositoryCustom {
List<Member> findMemberCustom();
}
- MemberRepositoryImpl
@RequiredArgsConstructor
public class MemberRepositoryImpl implements MemberRepositoryCustom{
private final EntityManager em;
@Override
public List<Member> findMemberCustom() {
return em.createQuery("select m from Member m")
.getResultList();
}
}
- MemberRepository
public interface MemberRepository extends JpaRepository<Member, Long>, MemberRepositoryCustom
이렇게 하면 내가 직접 Repository를 정의할 수 있다.
규칙이 하나 있다. MemberRepository에 대한 interface는 무조건 MemberRepository에 Impl을 붙여야 한다.
만약 MemberRepository 만으로 해결이 안되는 복잡한 쿼리나 동적 쿼리를 실행 할 때는 MemberRepositoryImpl을 만들어서 해결한다. (Querydsl + SpringJdbcTemplate)을 함께 사용할 때 사용자 정의 리포지토리 기능 자주 사용한다.
Auditing
엔티티를 생성, 변경할 때 변경한 사람과 시간을 추적하고 싶으면?
- 등록일
- 수정일
- 등록자
- 수정자
를 엔티티에 넣어야 한다.
순수 JPA 사용
@MappedSuperclass
public class JpaBaseEntity {
@Column(updatable = false)
private LocalDateTime createDate;
private LocalDateTime updateDate;
@PrePersist
public void prePersist(){
LocalDateTime now = LocalDateTime.now();
createDate = now;
updateDate = now;
}
@PreUpdate
public void preUpdate(){
updateDate = LocalDateTime.now();
}
}
그 후 Entity에 상속시키면 된다.
JPA 주요 이벤트 어노테이션
- @PrePersist, @PostPersist
- @PreUpdate, @PostUpdate
스프링 데이터 JPA 사용
- @EnableJpaAuditing 스프링 부트 설정 클래스에 적용해야한다.
- @EntityListeners(AuditingEntityListener.class) 엔티티에 적용해야 한다.
@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;
}
등록자, 수정자를 처리해주는 AuditorAware 스프링 빈 등록하면 사용자와 변경자를 알 수 있다.
@Bean
public AuditorAware<String> auditorProvider() {
return () -> Optional.of(UUID.randomUUID().toString());
}
실무에서 대부분의 엔티티는 등록시간, 수정시간이 필요하지만, 등록자, 수정자는 없을 수도 있다. 그래서 다음과 같이 Base 타입을 분리하고, 원하는 타입을 선택해서 상속한다. 강사님은
public class BaseTimeEntity {
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdDate;
@LastModifiedDate
private LocalDateTime lastModifiedDate;
}
public class BaseEntity extends BaseTimeEntity {
@CreatedBy
@Column(updatable = false)
private String createdBy;
@LastModifiedBy
private String lastModifiedBy;
}
이렇게 2개 나눠서 사용한다.
Web 확장 - 도메인 클래스 컨버터
HTTP 파라미터로 넘어온 엔티티의 아이디로 엔티티 객체를 찾아서 바인딩한다.
도메인 클래스 컨버터 사용 전
@RestController
@RequiredArgsConstructor
public class MemberController {
private final MemberRepository memberRepository;
@GetMapping("/members/{id}")
public String findMember(@PathVariable("id") Long id) {
Member member = memberRepository.findById(id).get();
return member.getUsername();
}
}
도메인 클래스 컨버터 사용 후
@RestController
@RequiredArgsConstructor
public class MemberController {
private final MemberRepository memberRepository;
@GetMapping("/members/{id}")
public String findMember(@PathVariable("id") Member member) {
return member.getUsername();
}
}
Member로 받아서 바로 사용할 수 있다.
Web 확장 - 페이징과 정렬
스프링 데이터가 제공하는 페이징과 정렬 기능을 스프링 MVC에서 편리하게 사용할 수 있다.
@GetMapping("/members")
public Page<Member> list(Pageable pageable){
Page<Member> page = memberRepository.findAll(pageable);
return page;
}
- 파라미터로 Pageable 을 받을 수 있다.
- Pageable 은 인터페이스, 실제는 org.springframework.data.domain.PageRequest 객체 생성
요청 파라미터
- /members?page=0&size=3&sort=id,desc&sort=username,desc
- page: 현재 페이지, 0부터 시작한다.
- size: 한 페이지에 노출할 데이터 건수
- sort: 정렬 조건을 정의한다. 예) 정렬 속성,정렬 속성...(ASC | DESC), 정렬 방향을 변경하고 싶으면 sort 파라미터 추가(asc 생략 가능)
기본값 변경
- 글로벌 설정 : 스프링 부트 (yml)
spring.data.web.pageable.default-page-size=20 /# 기본 페이지 사이즈/
spring.data.web.pageable.max-page-size=2000 /# 최대 페이지 사이즈/
- 개별 설정 : @PageableDefault 어노테이션을 사용한다.
@RequestMapping(value = "/members_page", method = RequestMethod.GET)
public String list(@PageableDefault(size = 12, sort = “username”,
direction = Sort.Direction.DESC) Pageable pageable) {
...
}
접두사
- 페이징 정보가 둘 이상이면 접두사로 구분된다.
- @Qualifier에 접두사명 추가 "{접두사명}_xxx" 으로 사용 가능하다.
Page 내용을 DTO로 변환하기
- 엔티티를 API로 노출하면 다양한 문제가 발생한다. 그래서 엔티티를 꼭 DTO로 변환해서 반환해야 한다.
- Page는 map() 을 지원해서 내부 데이터를 다른 것으로 변경할 수 있다
@GetMapping("/members")
public Page<MemberDto> list(Pageable pageable) {
Page<Member> page = memberRepository.findAll(pageable);
Page<MemberDto> pageDto = page.map(MemberDto::new);
return pageDto;
}
//CTRL+ALT+N 코드 최적확
@GetMapping("/members")
public Page<MemberDto> list(Pageable pageable) {
return memberRepository.findAll(pageable).map(MemberDto::new);
}
Page를 1부터 시작하기
1. Pageable, Page를 파리미터와 응답 값으로 사용히지 않고, 직접 클래스를 만들어서 처리한다. 그리고 직접 PageRequest(Pageable 구현체)를 생성해서 리포지토리에 넘긴다. 물론 응답값도 Page 대신에 직접 만들어서 제공해야 한다
PageRequest request = PageRequest.of(1,2);
2. spring.data.web.pageable.one-indexed-parameters 를 true 로 설정한다. 그런데 이 방법은 web에서 page 파라미터를 -1 처리 할 뿐이다. 따라서 응답값인 Page 에 모두 0 페이지 인덱스를 사용하는 한계가 있다.(결과값이 안맞는다)
하지만 pageIndex는 0부터 시작하는게 제일 깔끔하다.
출처
'Spring JPA 공부 > 스프링 데이터 JPA' 카테고리의 다른 글
나머지 기능들 (0) | 2023.01.25 |
---|---|
스프링 데이터 JPA 분석 (1) | 2023.01.23 |
@EntityGraph, JPA Hint @ Lock (0) | 2023.01.21 |
벌크성 수정 쿼리 (1) | 2023.01.20 |
JPA 페이징 처리 (0) | 2023.01.14 |