Table of Content
Spring Boot – JPA Paging
Spring Boot 의 JPA Paging 을 위한 방법을 설명합니다.
작동가능한 코드를 목적으로 하지 않고,
기본 원리를 정리하는 것을 목적으로 합니다.
아래 코드는 DB 접속 API 서버와 사용자 요청을 받는 서버가 분리된 것을 가정합니다.
Repository
Page<Entity>
를 리턴받습니다.
public interface CompanyRepositoryCustom {
Page<Company> search(SearchCompanyRequestDto dto);
}
Service
MapStruct 등을 이용해 DTO 로 변환합니다.
@Service
@RequiredArgsConstructor
public class CompanyService {
public ResponseEntity<?> search(SearchCompanyRequestDto dto) {
Page<CompanyDto> dtos = repository.search(dto).map(converter::toDto);
return ResponseEntity.ok(new ApiResponseWithData(ResponseCode.OK, dtos));
}
}
Controller
Page<DTO>
를 리턴해 줍니다.
@RestController
@RequiredArgsConstructor
@RequestMapping("/v1/account")
public class CompanyController {
@PostMapping("/search")
public ResponseEntity<?> search(@RequestBody SearchCompanyRequestDto dto) {
return service.search(dto);
}
}
의존성 추가
DB 접속 API 서버와 사용자 요청을 받는 서버가 분리된 상태라,
의존성을 추가로 넣어 줘야 합니다.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
}
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class
})
public class WarehousewebApplication {
public static void main(String[] args) {
SpringApplication.run(WarehousewebApplication.class, args);
}
}
커스텀 매퍼 생성
import org.springframework.data.domain.Page;
public class PageModule extends SimpleModule {
public PageModule() {
addDeserializer(Page.class, new PageDeserializer());
}
}
public class PageDeserializer extends JsonDeserializer<Page<?>> implements ContextualDeserializer {
private static final String CONTENT = "content";
private static final String NUMBER = "number";
private static final String SIZE = "size";
private static final String TOTAL_ELEMENTS = "totalElements";
private JavaType valueType;
@Override
public Page<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
final CollectionType valuesListType = ctxt.getTypeFactory().constructCollectionType(List.class, valueType);
List<?> list = new ArrayList<>();
int pageNumber = 0;
int pageSize = 0;
long total = 0;
if (p.isExpectedStartObjectToken()) {
p.nextToken();
if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) {
String propName = p.getCurrentName();
do {
p.nextToken();
switch (propName) {
case CONTENT:
list = ctxt.readValue(p, valuesListType);
break;
case NUMBER:
pageNumber = ctxt.readValue(p, Integer.class);
break;
case SIZE:
pageSize = ctxt.readValue(p, Integer.class);
break;
case TOTAL_ELEMENTS:
total = ctxt.readValue(p, Long.class);
break;
default:
p.skipChildren();
break;
}
} while (((propName = p.nextFieldName())) != null);
} else {
ctxt.handleUnexpectedToken(handledType(), p);
}
} else {
ctxt.handleUnexpectedToken(handledType(), p);
}
return new PageImpl<>(list, PageRequest.of(pageNumber, pageSize), total);
}
@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) {
final JavaType wrapperType = ctxt.getContextualType();
final PageDeserializer deserializer = new PageDeserializer();
deserializer.valueType = wrapperType.containedType(0);
return deserializer;
}
}
Feign Service
@FeignClient(name = "COMPANY-SERVICE",configuration = {FeignClientConfig.class, FeignRetryConfig.class}, url = "${api-server.url}:${api-server.port}")
public interface ApiCompanyService {
@PostMapping("/v1/account/search")
ApiResponseWithData search(
@RequestBody SearchCompanyRequestDto req
);
}
Service
@Service
@RequiredArgsConstructor
public class CompanyService {
private final ApiCompanyService apiCompanyService;
public Page<SearchCompanyResponseDto> search(SearchCompanyRequestDto req) {
ApiResponseWithData apiResponseWithData = apiCompanyService.search(req);
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new PageModule());
mapper.registerModule(new JavaTimeModule());
return mapper.convertValue(apiResponseWithData.getData(), new TypeReference<>() {});
}
}
Controller
Page<DTO>
를 변환합니다.
@Controller
public class CompanyIndexController {
@GetMapping("/admin/company/")
public String search(HttpServletRequest request, ModelMap model, @ModelAttribute SearchCompanyRequestDto req) {
Page<SearchCompanyResponseDto> searchCompanyResponseDtoPage = service.search(req);
model.addAttribute("list", searchCompanyResponseDtoPage.toList());
model.addAttribute("req", req);
model.addAttribute("currentPage", req.getPageNo());
model.addAttribute("maxPage", searchCompanyResponseDtoPage.getTotalPages());
return "/admin/company";
}
}