본문 바로가기

개인적으로 공부한 것을 정리해 놓은 블로그입니다 틀린 것이 있으면 댓글 부탁 드립니다!


JPA/Spring data JPA

SPRING DATA JPA 공부 4 - Query Method 활용 2 ( 정렬과 페이징)

반응형

 

 

이전 시간에 쿼리메소드에 대한 기본적인 활용에 대해 알아봤다 .

 

쿼리메소드를 통해 정렬과 페이징을 할 수 있는 방법을 알아보려한다 . 

 

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을 통해 설정 해줘야하는 것이 좋다 . 

 

데이터가 잘 출력 됬다 .  

 

 

반응형