참고
사용자 정의 레포지토리
NOTE
스프링 데이터 JPA는 인터페이스만 구현하면 스프링이 구현체를 자동으로 생성해준다.
•
하지만 스프링 데이터 JPA가 제공하는 인터페이스를 직접 구현하려면 구현해야하는게 너무 많다!
•
만약 어떠한 이유로 인터페이스의 메서드를 직접 구현해야 한다면?
◦
JPA 직접사용(EntityManager)
◦
스프링 JDBC Template 사용
◦
MyBatis 사용
◦
Querydsl 사용
•
스프링 JPA는 이런 경우를 위해서 사용자 정의 레포지토리 기능을 제공한다!
사용자 정의 레포지토리 구현
NOTE
사용자 정의 인터페이스 명 + Impl 방식으로 사용!
1. 사용자 정의 인터페이스
public interface MemberRepositoryCustom {
List<Member> findMemberCustom();
}
Java
복사
2. 사용자 정의 인터페이스 상속
@RequiredArgsConstructor
public class MemberRepositoryCustomImpl implements MemberRepositoryCustom {
private final EntityManager em;
@Override
public List<Member> findMemberCustom() {
return em.createQuery("select m from Member m")
.getResultList();
}
}
Java
복사
[ 참고]
•
실무에서는 주로 QueryDSL이나 SpringJdbtTemplate을 함께 사용할 때 사용자 정의 레포지토리 기능을 자주 사용한다.
•
항상 사용자 정의 레포지토리를 사용하기 보다는 여러 상황을 고려해서 사용하자!
Auditing
NOTE
협업할 떄 엔티티 생성, 변경한 시점과 사람의 기록을 남기는 것이 좋다!
•
등록일
•
수정일
•
등록자
•
수정자
순수 JPA 사용
NOTE
@MappedSuperclass
@Getter
public class JpaBaseEntity {
@Column(updatable = false)
private LocalDateTime createdDate;
private LocalDateTime updatedDate;
@PrePersist
public void prePersist(){
LocalDateTime now = LocalDateTime.now();
createdDate = now;
updatedDate = now;
}
@PreUpdate
public void preUpdate(){
updatedDate = LocalDateTime.now();
}
}
Java
복사
JPA 주요 이벤트 어노테이션
•
@PrePersist
◦
해당 엔티티를 저장하기 이전
•
@PostPersist
◦
해당 엔티티를 저장한 이후
•
@PreUpdate
◦
해당 엔티티를 업데이트 하기 이전
•
@PreUpdate
◦
해당 엔티티를 업데이트 한 이후
스프링 데이터 JPA 사용
NOTE
설정
•
@EnableJpaAudition
◦
스프링 부트 클래스에 적용
•
@EntityListeners(AuditiongEntityListener.class
◦
엔티티에 적용
스프링 데이터 Audition 적용
@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;
}
Java
복사
주요 어노테이션
•
@CreateDate
◦
데이터 생성 날짜 자동 저장 어노테이션
•
@LastModifiedDate
◦
데이터 수정 날짜 자동 저장 어노테이션
•
@CreatedBy
◦
데이터 생성자 자동 저장 어노테이션
•
@LastModifiedBy
◦
데이터 수정자 자동 저장 어노테이션
등록자, 수정자를 처리해주는 AuditorAware 스프링 빈 등록
@Bean
public AuditorAware<String> auditorProvider() {
return () ->
Optional.of(UUID.randomUUID().toString());
}
Java
복사
•
실무에서는 세션 정보나, 스프링 시큐리티 로그인 정보에서 ID를 받으면 된다.
Web 확장 - 도메인 클래스 컨버터
NOTE
HTTP 파라미터로 넘어온 엔티티의 아이디로 엔티티 객체를 찾아서 바인딩
도메인 클래스 컨버터
NOTE
적용전
@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();
}
}
Java
복사
파라미터로 아이디를 받아서 해당 아이디를 가진 엔티티를 조회한다!
적용후
@GetMapping("/members2/{id}")
public String findMember2(@PathVariable("id") Member member) {
return member.getUserName();
}
Java
복사
도메인 클래스 컨버터가 동작해서, 해당 엔티티를 객체로 반환해준다.
[ 참고]
•
단순히 조회용으로만 사용해야한다!
•
트랜잭션이 없는 범위에서 엔티티를 조회했으므로, 엔티티를 변경해도 DB에 반영되지 않음!
Web 확장 - 페이징과 정렬
NOTE
스프링 데이터 JPA가 제공하는 페이징과 정렬 기능을 스프링 MVC에서 편리하게 사용가능!
페이징과 정렬 예제
NOTE
@GetMapping("/members")
public Page<Member> list(Pageable pageable) {
Page<Member> page = memberRepository.findAll(pageable);
return page;
}
Java
복사
•
파라미터로 Pageable을 받을 수 있다.
•
Pageable은 인터페이스로, 실제로는 org.springframework.data.domain.PageRequest 객체를 생성한다.
실제 반환값!
페이징과 정렬
NOTE
1. 요청 파라미터
Ex) /members?page=0&size=3&sort=id,desc&sort=username,desc
Java
복사
쿼리 파라미터로 page, size, sort를 지정해줄 수 있다!
•
page
◦
현재 페이지 값
◦
0부터 시작한다.
•
size
◦
한 페이지에 노출할 데이터 건수
◦
기본 20, 최대 2000
•
sort
◦
정렬 조건을 정의한다.
◦
기본 asc
2. 접두사
public String list(
@Qualifier("member") Pageable memberPageable,
@Qualifier("order") Pageable orderPageable, ...
)
Java
복사
•
페이징 정보가 둘 이상이면, 접두사로 구분한다.
•
@Qualifier에 접두사명 추가 “{접두사명}_xxx”
◦
ex) /members?member_page=0&order_page=1
3. Page 내용을 DTO로 변환하기
@GetMapping("/members")
public Page<MemberDto> list(Pageable pageable){
Page<Member> page = memberRepository.findAll(pageable);
Page<MemberDto> map = page.map(member -> new MemberDto(member.getId(), member.getUserName(), null));
return map;
}
Java
복사
•
엔티티를 API로 노출하면 모든 정보가 노출되어 다양한 문제가 발생한다.
◦
그래서 꼭 엔티티를 DTO로 변환해서 반환해야 한다!
•
Page는 map()을 지원해서 내부 데이터를 다른 것으로 변경할 수 있다.