진취적 삶
JPA 단점과 JPQL 본문
JPA
public interface ProductRepository extends JpaRepository<Product, Long> {
Page<ReadProductResponse> findByProductNameContainingAndProductStateNot(
@Param("name") String name, @Param("state") String state, Pageable pageable);
}
상품검색에 대한 쿼리를 JPA 통해서 작성하면 메서드의 이름이 다음과 같이 길어져서 가독성이 해가된다.
이를 해결하기 위해서 JPQL 을 사용한다.
JPQL
JPQL 은 JAVA persistence query language 로
JPA 는 SQL 을 추상화한 JPQL이라는 객체 지향 쿼리 언어를 제공한다.
JPQL 은 SQL 을 추상화 했기 때문에 특정 DB SQL 에 의존하지 않는다.
JPQL 과 SQL 의 큰 차이점은 JQPL 은 엔티티 객체를 대상으로 쿼리을 작성하며 ,
SQL 은 DB 테이블을 대상으로 쿼리문을 작성하는것이다.
문제점 1
JPQL 은 기본 문자열로 작성되기 때문에 컴파일시 에러가 발생하지 않는다.
문제가 있음에도 불구하고 정상적으로 작동하여 런타임시 문제가 발생함.
@Query(value = "SELECT NEW com.dokebi.dalkom.domain.product.dto.ReadProductResponse( "
+ "p.productSeq, p.name, p.price, p.state, p.imageUrl, p.company, ps.productOption.detail, ps.amount)"
+ "FROM Product p "
+ "INNER JOIN ProductStock ps "
+ "ON p.productSeq = ps.product.productSeq "
+ "WHERE (p.name LIKE CONCAT('%', :name, '%') "
+ "AND p.state != 'N') "
+ "ORDER BY p.productSeq DESC , ps.productOption.prdtOptionSeq DESC ",
countQuery = "SELECT COUNT(p) FROM Product p ")
Page<ReadProductResponse> findProductListSearchByName(@Param("name") String name, Pageable pageable);
다음과 같이 JPQL 로 길게 작성하는 경우 @Query 안에는 문자열이기 때문에 에러 확인을 바로 할수 없다.
그리고 현재 DTO를 바로 반환하기 위해서 DTO 프로젝션을 사용하고 있는데 JPQL 코드안에가 더러워지는것을 볼수 있다.
문제점 2
controller
@GetMapping("/api/admin/search")
@ResponseStatus(HttpStatus.OK)
public Response readAdminListSearch(
@RequestParam(required = false) String name,
@RequestParam(required = false) String adminId,
@RequestParam(required = false) String depart,
@RequestParam(required = false) String nickname, Pageable pageable) {
return Response.success(adminService.readAdminListSearch(name, adminId, depart, nickname, pageable));
}
service
public Page<ReadAdminResponse> readAdminListSearch(String name, String adminId, String depart, String nickname,
Pageable pageable) {
if (name != null) {
return adminRepository.findAdminListByName(name, pageable);
} else if (adminId != null) {
return adminRepository.findAdminListByAdminId(adminId, pageable);
} else if (depart != null) {
return adminRepository.findAdminListByDepart(depart, pageable);
} else if (nickname != null) {
return adminRepository.findAdminListByNickname(nickname, pageable);
} else {
// 다른 조건이 없는 경우 기본적인 조회 수행
return adminRepository.findAllAdminList(pageable);
}
}
repository
@Query("SELECT new com.dokebi.dalkom.domain.admin.dto.ReadAdminResponse(" +
"a.adminSeq, a.adminId, a.role, a.nickname, a.name, a.depart) " +
"FROM Admin a WHERE a.name LIKE CONCAT('%', :name, '%')")
Page<ReadAdminResponse> findAdminListByName(
@Param("name") String name, Pageable pageable);
@Query("SELECT new com.dokebi.dalkom.domain.admin.dto.ReadAdminResponse("
+ "a.adminSeq, a.adminId, a.role, a.nickname, a.name, a.depart) "
+ "FROM Admin a WHERE (a.adminId LIKE CONCAT('%', :adminId, '%')) ")
Page<ReadAdminResponse> findAdminListByAdminId(String adminId, Pageable pageable);
@Query("SELECT new com.dokebi.dalkom.domain.admin.dto.ReadAdminResponse("
+ "a.adminSeq, a.adminId, a.role, a.nickname, a.name, a.depart) "
+ "FROM Admin a WHERE (a.depart LIKE CONCAT('%', :depart, '%')) ")
Page<ReadAdminResponse> findAdminListByDepart(@Param("depart") String depart, Pageable pageable);
@Query("SELECT new com.dokebi.dalkom.domain.admin.dto.ReadAdminResponse("
+ "a.adminSeq, a.adminId, a.role, a.nickname, a.name, a.depart) "
+ "FROM Admin a WHERE (a.nickname LIKE CONCAT('%', :nickname, '%')) ")
Page<ReadAdminResponse> findAdminListByNickname(@Param("nickname") String nickname, Pageable pageable);
관리자 검색 관련 코드이다 살펴보면 각각의 필터링된 값을 검색하기위해서 나눠서 repository 메서드를 만든것을 볼수 있다.
현재 name,adminId,Depart,nickname 으로 필터링하고 있다.
만약 필터링하는 개수가 10~ 20개 로 늘어나는 경우
repository에 해당하는 필터링에대해서 코드를 계속 작성을 해줘야 한다.
따라서 컴파일시 에러처리와 코드 가독성 향상과 검색 처리를 위한 동적 쿼리를 위한 QueryDSL 을 적용해 보자 .
'개발' 카테고리의 다른 글
QueryDSL (0) | 2024.02.28 |
---|---|
JPA 와 Hibernate (0) | 2024.02.28 |
docker 기반 nginx 를 이용한 로드 밸런싱 (1) | 2024.01.24 |
docker를 이용한 3tier 구축하기 nginx ,tomcat,db (1) | 2024.01.18 |