이전 시간에 쿼리메소드에 대한 기본적인 활용에 대해 알아봤다 .
쿼리메소드를 통해 정렬과 페이징을 할 수 있는 방법을 알아보려한다 .
repository에 3가지 메소드를 만들었다
1.정렬
List<User> findTop3ByNameOrderByIdDesc(String name);
List<User> findFirstByNameOrderByIdDescEmailAsc(String name);
List<User> findFirstByName(String name , Sort sort);
fintTop3는 이름순으로 내림차순으로 정렬하여 그 중 가장위의 3개 레코드를 얻어오느 메서드이다 메서드이름에 OrderByIdDesc가 그것을 의미한다 .
findFirstByNameOrderByIdDescEmailAsc 이름이 엄청길다 이 메서드 처럼 여러가지 정렬 값을 넣어줄 수도 있다 .
findFirstByName 메서드는 깔끔하다 메서드명에 정렬에 대한 내용이 들어가 있지않고 파라미터로 Sort 객체를 받아 활용한다 .
테스트 코드를 만들어 활용해보자
@Test
void sortingWithQueryMethod(){
//Top3
List<User> users = userRepository.findTop3ByName("ugo");
users.forEach(user -> {
System.out.println("Top3: " + user);
});
//여러가지 정렬을 메서드이름에 정의
List<User> IdDescEmailAsc = userRepository.findFirstByNameOrderByIdDescEmailAsc("ugo");
IdDescEmailAsc.forEach(user -> {
System.out.println("IdDescEmailAsc: " + user);
});
//Sort 생성해서 넘겨준다. Order에는 여러가지 넘겨줄 수 있다.
List<User> firstByNameSort = userRepository.findFirstByName("ugo", Sort.by(desc("id"),asc("email")));
firstByNameSort.forEach(user -> {
System.out.println("firstByNameSort: " + user);
});
}
마지막 Sort 생성을 보자 Sort.by를 통해 Sort 객체를 만들어 내고 파라미터로 원하는 정렬조건을 지정했다 .
위의 같은 경우에는 id에 대한 내림차순 , email 에대한 오름 차순으로 넘겼다 (위에선 static import 를 통해 desc() , asc() 로 적혀있지만 원래는 Order.asc() , Order.desc()의 형태이다 . )
2.페이징
페이징은 기본적으로 요청과 응답이 있을 것이다 .브라우저에서 페이지 번호 , 한 페이지에 표시 할 갯수 , 정렬 조건을 요청하면
해당 요청에 대한 결과 데이터를 응답해주는 구조일 것이다 . 처음 JSP를 배웠을때는 페이징 관련 로직이 적어도 50줄 이상이었던 것 같다.
JPA에서는 페이징의 요청과 , 응답에 대한 데이터를 계산해서 리턴해주는 Page(응답) , Pagable(요청) 객체를 제공한다 .
Page와 Pagable을 활용해 페이징 처리를 해보자
//Page 객체는 페이징 응답값을 담고 Pageble 은 페이지 요청 값을 담는다.
Page<User> findByName(String name, Pageable pageable);
repository의 메서드이다 리턴값을 Page로 하고 요청 데이터를 Pageable로 제공한다 .
테스트 코드로 해당 메서드를 사용해 보자 . 먼저 실제 응답을 한다 생각하고 Object를 생성하여 응답에 대한 값을 담고 ObjectMapper를 통해 json 문자열로 바꿔보는 것까지 해봤다 .
PageDto
import org.springframework.boot.autoconfigure.data.web.SpringDataWebProperties;
import org.springframework.data.domain.PageRequest;
import java.util.ArrayList;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class PageDto {
private int numberOfElements;
private int totalElements;
private int totalPages;
private List<Content> contents = new ArrayList<>();
}
Content
package com.ugo.jpatest.dto;
import lombok.*;
import javax.transaction.Transactional;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class Content {
private Long id;
private String name;
private String email;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
메서드를 통해 리턴되는 Page에 대한 정보를 담을 Dto를 만들었다 .
페이징에 대한 정보(총 레코드 수 , 총 페이지 수 , 한 페이지에 담기는 element의 갯수 )와 , 검색된 컨텐츠를 List 형식으로 담는다 .
이번에는 그냥 Content 클래스를 따로 만들었지만 위와 같이 Content가 PageDto에 대하여 종속적인 관계를 갖을 때는 static 내부 클래스로 Content 객체를 만드는 것이 좋다고 한다 .
한 가지 중요한 점은 Page 객체안에 Contents는 Page안에 제네릭타입으로 지정된 엔티티 클래스의 형태로 Return 하는데 해당 엔티티를 바로 응답하게 되면 해당 애플리케이션에 엔티티 구조 같은 것이 노출 될 수 있기 때문에 엔티티를 바로 응답하기보다는 해당 엔티티에 대한 정보를 담을 Dto를 만들어 응답하는 것이 좋은 방식이라 한다 .
repository의 메서드를 활용해서 페이징 처리를 해보자
@Test
void pagingWithQueryMethod() throws JsonProcessingException {
//PageRequest로 page 요청정보를 담는고 리턴되는 데이터를 Page 객체에 담는다.
Page<User> page = userRepository.findByName("ugo", PageRequest.of(0, 2, Sort.by(desc("id"))));
//page에서 컨텐츠 정보를 꺼내 Content 객체에 담는다
List<Content> contents = new ArrayList<>();
page.getContent().forEach(content->{
Content cont = new Content();
cont.setCreatedAt(content.getCreatedAt());
cont.setEmail(content.getEmail());
cont.setId(content.getId());
cont.setName(content.getName());
cont.setUpdatedAt(content.getUpdatedAt());
contents.add(cont);
});
//page에서 가져온 페이징 정보와 , 컨텐츠 정보를 담은 List<Content>를
//PageDto에 담는다 이름이 좀 별로인 것 같다.
PageDto pageDto = new PageDto();
pageDto.setContents(contents);
pageDto.setTotalPages(page.getTotalPages());
pageDto.setNumberOfElements(page.getNumberOfElements());
pageDto.setTotalElements(pageDto.getTotalElements());
//Map에 담으면 어떻게 나올지 궁금해서 한번 해봤다.
Map<String,Object> jsonPage = new HashMap<>();
jsonPage.put("json_data",pageDto);
jsonPage.put("createdBy","ugo");
//Map을 json String으로 변환
String pageResponse = objectMapper.writeValueAsString(jsonPage);
//json node 접근 이것도 궁금해서 한번해 봤다 .
JsonNode jsonNode = objectMapper.readTree(pageResponse);
System.out.println("createdBy = " + jsonNode.findValue("createdBy"));
//json String 으로 변환된 pageDto를 보기 좋게 변환.
System.out.println(JsonFormatter.prettyPrint(pageResponse));
}
코드 설명은 주석으로 적혀 있기 때문에 따로 설명하지 않고 , 콘솔에 어떻게 찍히는지 한번보겠다
리턴 json 데이터
{
"created_by" : "ugo",
"json_data" : {
"number_of_elements" : 2,
"total_elements" : 0,
"total_pages" : 3,
"contents" : [
{
"id" : 5,
"name" : "ugo",
"email" : "ugo5.com",
"created_at" : "2021-05-28T16:21:39.056595",
"updated_at" : "2021-05-28T16:21:39.056605"
},
{
"id" : 4,
"name" : "ugo",
"email" : "ugo4.com",
"created_at" : "2021-05-28T16:21:39.053759",
"updated_at" : "2021-05-28T16:21:39.053769"
}
]
}
}
json 데이터는 키값을 snake case 로 한다고 하니 해당 Dto에 @JsonProperty나 @JsonNaming을 통해 설정 해줘야하는 것이 좋다 .
데이터가 잘 출력 됬다 .
'JPA > Spring data JPA' 카테고리의 다른 글
SPRING DATA JPA 공부 7 - AuditingEntityListener 사용 (0) | 2021.05.29 |
---|---|
SPRING DATA JPA 공부 5 - EntityListener 활용 (0) | 2021.05.29 |
SPRING DATA JPA 공부 3 - QueryMethod 활용 (0) | 2021.05.27 |
SPRING DATA JPA 공부 2- JPA 메서드 살펴보기 (0) | 2021.05.27 |
SPRING DATA JPA 공부 1 - JPA 란? (0) | 2021.05.27 |