본문 바로가기
SpringBoot Server/API

JPA Pageable을 활용하여 전체 데이터 조회하기 - JDBC와 비교

by ssury94 2025. 1. 6.
 
 
 

 

JPA란?

https://maeilcoding.tistory.com/136

 

SQL 대신 자바로 DB와 상호작용하기 JPA(Java Persistence API)

지금까지는 JDBC를 활용하여 DAO 클래스에서 SQL쿼리를 통해 DB 작업을 하였는데요.//sql을 통한 회원가입 처리@Repositorypublic class UserDAO { @Autowired JdbcTemplate jdbcTemplate; // todo 회원가입 public int signUp(User

maeilcoding.tistory.com

 

 

JDBCJPA의 매커니즘 차이점에 주목하여 작성해보려 했습니다.

 


RepositoryClass

@Repository
public interface UserRepository extends JpaRepository<User,Long> {
    
}

JpaRepository를 상속받아 JPA 제공 메서드들을 사용할 수 있습니다.

 

ServiceClass

// * ServiceClass

// 모든 주문 조회
public OrderListResponse getAllOrders(int page, int size, String order) {
    //order 변수는  : orderDate,desc 주문일 최신순으로

    String[] orderArray = order.split(",");
    //,를 기준으로 나눠줌
    //OrderArray[0] = "orderDate"
    //OrderArray[1] = "desc"

    Sort.Direction direction = Sort.Direction.fromString(orderArray[1]);
    // //OrderArray[1] = "desc"를 Direction 으로 변환

    Sort sort = Sort.by(direction, orderArray[0]);
    PageRequest pageRequest = PageRequest.of(page - 1, size, sort);

    // JPA 매뉴얼에 있는 내용

    Page<Order> orderPage = orderRepository.findAll(pageRequest);
    // DB 에서 데이터를 가져옴 sql 의 limit 와 같음

    ArrayList<OrderItemResponse> orderList = new ArrayList<>();
    //Order 에서 가져온 데이터를 DTO 로 변환
    //for 문을 사용해서 데이터를 하나씩 꺼내서 DTO 로 변환

    for( Order savedOrder : orderPage){
        // orderPage 에 있는 데이터를 하나씩 꺼내서 savedOrder 에 넣어줌
        OrderItemResponse orderItemResponse=new OrderItemResponse();
        orderItemResponse.id=savedOrder.id;
        orderItemResponse.productName=savedOrder.productName;
        orderItemResponse.quantity=savedOrder.quantity;
        orderItemResponse.totalPrice=savedOrder.totalPrice;
        orderItemResponse.orderDate=savedOrder.orderDate.toString();
        // for 로 반복해서 리스트에 하나씩 넣어준다
        orderList.add(orderItemResponse);
    } // 반복문 완료되면 orderList 에 데이터가 들어있음

            //"pageInfo" 준비 > PageInfo DTO 만들기
    PageInfo pageInfo =new PageInfo();
    pageInfo.page=page;
    pageInfo.size=size;
    // 클라이언트가 보낸 page 와 size 를 pageInfo 에 넣어주면됨
    pageInfo.totalElements=orderPage.getTotalElements();
    //orderRepository.findAll(pageRequest) 의 데이터 갯수를 가져옴
    //orderPage 에 totalElements, totalPage 둘 다 JPA 가 처리해줬음
    //대신 Int 가 아닌 Long 으로 처리됨 > PageInfo DTO의 데이터 타입 변경
    pageInfo.totalPages=orderPage.getTotalPages();

    //pageInfo 랑 orderList 담아줄 OrderListResponse DTO 만들기
    OrderListResponse orderListResponse = new OrderListResponse(orderList, pageInfo);
    return orderListResponse;
}

 

 

정렬 파라미터 처리

String[] orderArray = order.split(",");
//,를 기준으로 나눠줌
//OrderArray[0] = "orderDate"
//OrderArray[1] = "desc"

Sort.Direction direction = Sort.Direction.fromString(orderArray[1]);
// //OrderArray[1] = "desc"를 Direction 으로 변환

Sort sort = Sort.by(direction, orderArray[0]);

파라미터를 asc, desc 처리하기

파라미터 중 orderDate,desc는 order by order_date desc로 작동해야 합니다.

,을 기준으로 나누어 작동할 수 있게 쿼리작성

Sort.by()

orderDate 필드를 기준으로 설정하고 위에서 지정한 방향(Direction = desc)으로 정렬하게 합니다.

SQL 쿼리에서 Order by절 역할!

 

 

Pageable 사용하여 페이징 처리

JDBC에서는 총 페이지, offset, Elements 수 계산 등의 페이징 처리를 직접 구현했었는데요. 

//* JDBC
// 총 페이지 계산
int totalPages = (int) Math.ceil(totalElements / (double) size);

// pageable 객체 생성
PageableResponse pageable = new PageableResponse(page, size, totalElements, totalPages);

//offset
int offset = (page - 1) * size;

//총 요소 수 계산
public int getTotalElements () {
        String sql = "select count(*)\n" +
                "from restaurant;";
        return jdbcTemplate.queryForObject(sql, Integer.class);

 

JPA에서는 Pageable 인터페이스를 사용하여 자동으로 처리할 수 있습니다.

PageRequest.of()

PageRequest pageRequest = PageRequest.of(page - 1, size, sort);

Spring Data JPA에서 페이지네이션을 구현하는 유용한 메서드

페이지 번호와 페이지 크기, 정렬을 기반으로 PageRequest 객체 생성

 

Pageable

Pageable은 Spring Data JPA에서 제공하는 인터페이스로, 페이징(Paging)과 정렬(Sorting) 기능을 간편하게 구현할 수 있도록 도와줍니다. 데이터베이스에서 대량의 데이터를 효율적으로 조회할 때 유용합니다. 예를 들어, 한 번에 모든 데이터를 가져오는 대신, 특정 페이지에 해당하는 데이터만 조회할 때 사용됩니다.

PageRequest는 이 인터페이스의 구현체로, 페이지 번호와 페이지 크기 등의 정보를 포함

 

page는 첫번째 페이지 = 인덱스 0번이 올 수 있도록 -1 처리 합니다.

 

주문 데이터 조회

JDBC에서는 DAO에서 SQL 쿼리로 불러온 데이터의 각 행을 RowMapper() 인터페이스를 통해 Java객체로 변환했는데요.

JPA에서는 Entity 클래스를 통해 자동으로 매핑됩니다.

// * JDBC
// 음식점 별 메뉴 리스트 조회
public List<MenuResponse>getMenuListByRestaurantId(Long restaurantId) {
    String sql = "SELECT m.id, m.name ,m.price ,m.description ,m.category, count(r.menu_id) as reviewCount\n" +
            "FROM menu m \n" +
            "left join review r \n" +
            "on m.id =r.menu_id \n" +
            "where m.restaurant_id  = ?\n" +
            "group by m.id;\n";
    return jdbcTemplate.query(sql, new MenuRowMapper(), restaurantId);
}

    public static class MenuRowMapper implements RowMapper<MenuResponse> {

        @Override
        public MenuResponse mapRow(ResultSet rs, int rowNum) throws SQLException {
            MenuResponse menuResponse = new MenuResponse();
            menuResponse.id = rs.getLong("id");
            menuResponse.name = rs.getString("name");
            menuResponse.price = rs.getInt("price");
            menuResponse.description = rs.getString("description");
            menuResponse.category = rs.getString("category");
            menuResponse.reviewCount = rs.getInt("reviewCount");
            return menuResponse;
        }
    }

 orderRepository.findAll()

Page<Order> orderPage = orderRepository.findAll(pageRequest);

JPA에서는 리포지토리메서드 중 하나인 .findAll 메서드는 모든 엔티티 레코드를 가져오는 SQL 쿼리를 실행합니다.

 

여기에 pageRequest 객체를 사용하면 페이지 번호, 페이지 크기, 정렬기준을 설정할 수 있습니다.

SELECT *
FROM `order` o 
order by order_date desc
LIMIT 0,10;

 

SQL쿼리로는 위와 같은 형태입니다.

 

SQL 쿼리 실행시 결과 == orderPage

 

OrderItemResponse DTO 변환

JPA가 가져온 orderPage를 OrderItemRespose로 변환합니다.

ArrayList- for문 사용

ArrayList<OrderItemResponse> orderList = new ArrayList<>();
//Order 에서 가져온 데이터를 DTO 로 변환
//for 문을 사용해서 데이터를 하나씩 꺼내서 DTO 로 변환

for (Order savedOrder : orderPage) {
    // orderPage 에 있는 데이터를 하나씩 꺼내서 savedOrder 에 넣어줌
    OrderItemResponse orderItemResponse = new OrderItemResponse();
    orderItemResponse.id = savedOrder.id;
    orderItemResponse.productName = savedOrder.productName;
    orderItemResponse.quantity = savedOrder.quantity;
    orderItemResponse.totalPrice = savedOrder.totalPrice;
    orderItemResponse.orderDate = savedOrder.orderDate.toString();
    // for 로 반복해서 리스트에 하나씩 넣어준다
    orderList.add(orderItemResponse);
} // 반복문 완료되면 orderList 에 데이터 목록이 들어감

 

 

PageInfo DTO 변환

//"pageInfo" 준비 > PageInfo DTO 만들기
PageInfo pageInfo = new PageInfo();
pageInfo.page = page;
pageInfo.size = size;
// 클라이언트가 보낸 page 와 size 를 pageInfo 에 넣어주면됨
pageInfo.totalElements = orderPage.getTotalElements();
//orderRepository.findAll(pageRequest) 의 데이터 갯수를 가져옴
//orderPage 에 totalElements, totalPage 둘 다 JPA 가 처리해줬음
//대신 Int 가 아닌 Long 으로 처리됨 > PageInfo DTO 의 데이터 타입 변경
pageInfo.totalPages = orderPage.getTotalPages();

pageInfo : 현재 페이지, 페이지 크기, 총 요소 수 및 총 페이지 수 도 전달해주는 DTO를 만듭니다.

 

 

페이지 정보 준비

//pageInfo 랑 orderList 담아줄 OrderListResponse DTO 만들기
OrderListResponse orderListResponse = new OrderListResponse(orderList, pageInfo);
return orderListResponse;

OrderListResponse 객체를 생성해

orderList와 pageInfo를 담아 반환합니다.

 

Controller Class

@GetMapping("/orders")
public ResponseEntity<OrderListResponse> getAllOrders(@RequestParam int page, @RequestParam int size, @RequestParam String order){
    OrderListResponse orderListResponse = orderService.getAllOrders(page, size, order);
return ResponseEntity.status(200).body(orderListResponse);
}

OrderListRespose로 반환합니다.

 

 

Postman Response 확인

 

 

 

 

 

JDBC와 JPA와의 비교

  JDBC JPA
데이터 매핑 RowMapper를 사용하여 ResultSet을 객체로 변환 Entity 클래스를 통해 자동으로 매핑
쿼리 방식 SQL 쿼리를 직접 작성 JPQL 또는 메서드 이름 기반 쿼리 사용
코드 간결성 반복적인 매핑 로직 필요 간결하고 직관적인 코드
사용사례 복잡한 쿼리나 성능 최적화가 필요한 경우에 유리  빠른 개발과 유지보수성을 중시하는 경우에 유리

 

JDBC와 JPA는 각각의 장단점이 있으며, 선택은 프로젝트의 요구사항과 팀의 기술 스택에 따라 달라질 수 있습니다.