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
JDBC와 JPA의 매커니즘 차이점에 주목하여 작성해보려 했습니다.
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쿼리로는 위와 같은 형태입니다.
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는 각각의 장단점이 있으며, 선택은 프로젝트의 요구사항과 팀의 기술 스택에 따라 달라질 수 있습니다.