** 국비교육 : 중앙정보처리기술인재개발원에서 진행한 팀프로젝트 내용입니다.
기록용으로 남기며, 참고만 부탁드립니다.
** https://github.com/no-nangbie
● JPQL 이란?
JPQL은 JPA에서 사용하는 객체 지향 쿼리 언어이다.
데이터베이스 테이블을 대상으로 CRUD하는 SQL과 다르게 JPQL은 엔티티를 대상으로 실행된다.
초반에 이 점에 대해 정확하게 인지하지 못했기에 많은 시행착오가 있었다.
좀 더 정확한 정보를 얻고자 SQL과 JPQL의 차이점에 대해서 알아보았다.
비교 항목 | JPQL | SQL |
대상 | 엔티티 ( @Entity 클래스 ) | 데이터베이스 테이블 |
필드 접근 | 엔티티의 필드 사용 (user.name) | 컬럼명 사용 (user_table.name) |
반환 타입 | 엔티티 객체, DTO, 특정 필드 값 | 행(row) 결과 |
데이터베이스 종속성 | 데이터베이스 벤더 독립적 | 특정 DB에 종석 |
작성되는 쿼리문을 예시로 드는 것이 조금 더 직관적으로 사용방식의 차이를 볼 수 있다.
1. 데이터베이스 테이블 OR 엔티티 안에서의 'Tizesin' 이라는 이름을 가진 사람을 조회
SQL | SELECT * FROM user_table u WHERE u.name = 'Tizesin'; |
JPQL | SELECT u FROM User u WHERE u.name = 'Tizesin' |
● JPQL 사용 방법
JpaRepository 를 extends 한 interface Repository에서 특정 데이터를 불러 올 때 아래와 같이 규칙이 있다.
package com.nonangbie.member.repository;
import com.nonangbie.member.entity.Member;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
public interface MemberRepository extends JpaRepository<Member, Long> {
Optional<Member> findByEmail(String email);
Optional<Member> findByNickname(String nickname);
boolean existsByEmail(String email);
boolean existsByNickname(String nickName);
Optional<Member> findByEmailAndNickname(String email, String nickname);
}
위 소스를 보면 아래와 같이 해석할 수 있다.
find : 찾는다
By : 무엇을
Email : 이메일을
And : 그리고
Nickname : 닉네임을
반환값이 Optional<Member> 일 경우 해당 데이터에 맞는 Member Entity를 가져와준다.
없을 경우에는 null을 리턴한다.
find 대신 exists는 데이터의 존재 유무를 파악해서 true와 false를 반환해 줄 수 도 있다.
그치만 위의 방법으로 SQL의 복잡한 쿼리문을 작성하기에는 제한적이다.
이럴 때, 따로 JPQL문을 작성하여 특수 목적의 인터페이스 메서드를 만들 수 있다.
package com.nonangbie.menu.repository;
import com.nonangbie.menu.entity.Menu;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface MenuRepository extends JpaRepository<Menu, Long> {
@Query("SELECT m FROM Menu m WHERE (m.menuTitle LIKE %:keyword%) AND m.menuCategory = :menuCategory")
Page<Menu> searchByMenuTitleAndCategory(Pageable pageable, @Param("keyword") String keyword, @Param("menuCategory") Menu.MenuCategory menuCategory);
@Query("SELECT m FROM Menu m WHERE "
+ "(:keyword IS NULL OR m.menuTitle LIKE CONCAT('%', :keyword, '%')) AND "
+ "(:menuCategory IS NULL OR m.menuCategory = :menuCategory) AND "
+ "(:foodId IS NULL OR EXISTS (SELECT n FROM FoodMenu n WHERE n.menu = m AND n.food.foodId = :foodId))")
Page<Menu> findAllMenusIntegration(Pageable pageable,
@Param("menuCategory") Menu.MenuCategory menuCategory,
@Param("keyword") String keyword,
@Param("foodId") Long foodId); // foodId 타입을 Long으로 변경
@Query("SELECT m FROM Menu m " +
"WHERE m.menuCategory IN :menuCategoryList")
List<Menu> findAllByRecommendations(@Param("menuCategoryList") List<Menu.MenuCategory> menuCategoryList);
}
@Query 어노테이션을 통해 JPQL을 구현할 수 있다.
위와 같이 작성할 경우 복잡한 쿼리문을 해결할 수 있다.
● JPQL 을 활용하면서 얻게 된 이점
JpaRepository의 정해진 규칙으로 작성하기에 제한적인 복잡한 쿼리문을 JPQL로 한번에 구현할 수 있어 구현력에 큰 이점을 가질 수 있었다.
● 팀 프로젝트를 진행하면서..
JPQL로 복잡한 쿼리문을 작성하는 부분에서 많이 사용하고 있다. 그러나, 동적 쿼리문을 사용해야하는입장에서는 어쩔 수 없이 JPQL의 쿼리문이 복잡해지고 유지보수성 및 가시성이 하락하는 문제가 생겼다.
이를 해결하기 위해 새로운 방안을 모색해야겠다고 생각이 들었다.
'Project' 카테고리의 다른 글
[ Project : 낭비없냉 ] #4 프로젝트를 마치며 (0) | 2024.09.23 |
---|---|
[ Project : 낭비없냉 ] #3 AWS S3 Upload (0) | 2024.09.13 |
[ Project : 낭비없냉 ] #1 기획과 설계 (0) | 2024.09.01 |
[ Project : 함께걷개 ] #3 프로젝트를 마치며 (0) | 2024.08.30 |
[ Project : 함께걷개 ] #2 Scheduler / EventListener (0) | 2024.08.16 |