public interface MemberRepository extends JpaRepository<Member,Long> , MemberRepositoryCustom
public interface MemberRepositoryCustom
{
List<Member> findMemberCustom();
}
@RequiredArgsConstructor
public class MemberRepositoryImpl implements MemberRepositoryCustom
{
private final EntityManager em;
@Override
public List<Member> findMemberCustom()
{
return em.createQuery("select m from Member m")
.getResultList();
}
}
기존 Repository 말고 상당히 복잡하고 화면 specifc 한 쿼리를 짜야한다면 따로 Repository를 분리해서 만드는것이 유지보수하기 편하다.
Auditing
엔티티를 생성, 변경할 때 변경한 사람과 시간을 추적하고 싶을 때 (운영을 위해서)
순수 JPA방법
@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();
}
}
public class Member extends JpaBaseEntity
추적하고 싶은 엔티티에게 extends를 해주면 속성들만 Member table에 들어와서 쓸 수 있게 된다.
Spring Data JPA 를 이용해서 Auditing 하는 방법
@EnableJpaAuditing
@SpringBootApplication
public class SpringDataJpaApplication {
public static void main(String[] args) {
SpringApplication.run(SpringDataJpaApplication.class, args);
}
@Bean
public AuditorAware<String> auditorProvider()
{
return () -> Optional.of(UUID.randomUUID().toString());
}
}
@EnableJpaAuditing 을 까먹으면 안된다.
AuditorAware에서 지금은 randomUUID를 return 하지만 실무에서 예를 들면 시큐리티를 쓴다면 해당 사람의 아이디를 return 해줘야한다.
@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;
}
@EntityLinsters가 추가되었고 이제 수정자와 생성자 (누가했는가?) 를 넣어줄 수 있게 되었다.
여기에 들어가는 값은 위에서 return 한 randomUUID이다.
시간과 사람을 분리해서 부모(시간) 자식(사람) 관계로 묶을 수 있다. 둘다 @MappedSuperclass를 써줘야한다.
Web 확장 - 도메인 클래스 컨버터
@GetMapping("/members/{id}")
public String findMember (@PathVariable("id") Long id)
{
Member member = memberRepository.findById(id).get();
return member.getUsername();
}
@GetMapping("/members2/{id}")
public String findMember (@PathVariable("id") Member member)
{
return member.getUsername();
}
@PostConstruct
public void init()
{
memberRepository.save(new Member("userA"));
}
알아서 Member를 given id로 조회하는데 이것은 간단한 조회할 떄만 쓰는것이다.
난 햇갈리니 그냥 위의 방법을 쓰는게 명시적으로 안햇갈릴것같다. 아래는 원인모를 에러가 발생할 수 있다 했다.
Web 확장 - 페이징과 정렬
@GetMapping("/members")
public Page<Member> list(Pageable pageable)
{
Page<Member> page = memberRepository.findAll(pageable);
return page;
}
@PostConstruct
public void init()
{
for(int i = 0; i < 100; i++)
{
memberRepository.save(new Member("user"+i,i));
}
}
spring:
data:
web:
pageable:
default-page-size: 10
max-page-size: 2000
이것은 application.yml 에서 global 하게 페이지 사이즈를 setting 한 것이다.
@GetMapping("/members")
public Page<Member> list(@PageableDefault(size = 5, sort = "username") Pageable pageable)
{
Page<Member> page = memberRepository.findAll(pageable);
return page;
}
개인적으로 설정하고싶으면 @PageableDefault로 설정이 가능하다. 이것이 global 설정보다 우선권을 가지게 된다.
localhost:8083/members?page=0&size=3&sort=id,desc&sort=username,desc
postman에서 이런식으로 request를 날려주면
{
"content": [
{
"id": 100,
"username": "user99",
"age": 99,
"team": null
},
{
"id": 99,
"username": "user98",
"age": 98,
"team": null
},
{
"id": 98,
"username": "user97",
"age": 97,
"team": null
}
],
"pageable": {
"sort": {
"sorted": true,
"unsorted": false,
"empty": false
},
"offset": 0,
"pageNumber": 0,
"pageSize": 3,
"paged": true,
"unpaged": false
},
"last": false,
"totalPages": 34,
"totalElements": 100,
"size": 3,
"number": 0,
"sort": {
"sorted": true,
"unsorted": false,
"empty": false
},
"first": true,
"numberOfElements": 3,
"empty": false
}
이렇게 알아서 PageRequest를 주어진 request를 파싱해서 적절히 생성해주어서 Pageable pageable에 넣어준다.
@GetMapping("/members")
public Page<MemberDto> list(@PageableDefault(size = 5, sort = "username") Pageable pageable)
{
Page<Member> page = memberRepository.findAll(pageable);
Page<MemberDto> result = page.map(member -> new MemberDto(member.getId(), member.getUsername(), null);
return result;
}
항상 Dto를 이용해서 return 해줘야한다.
접두사
public String list
(
@Qualifer("member") Pageable memberPageable,
@Qualifer("order") Pageable orderPageable
)
/members?member_page=0&order_page=1
이런식으로 한번에 여러게 Page를 받아와야할 떄는 @Qualifer에 접두사명을 추가해주면된다.
'WEB > JPA' 카테고리의 다른 글
실전! QueryDsl 1,2 (설정 ,문법) (0) | 2021.04.16 |
---|---|
실전! Spring Data JPA 4,5 (스프링 데이터 JPA 분석, 나머지 기능들) (0) | 2021.04.14 |
실전! Spring Data JPA 1,2(공통 인터페이스 기능, 쿼리 메소드 기능) (0) | 2021.04.12 |
실전! 스프링부트와 JPA와 활용2 (OSIV와 성능 최적화) (0) | 2021.04.06 |
실전! 스프링부트와 JPA와 활용2 (컬렉션 조회 최적화) (0) | 2021.04.06 |