
: 쿼리 메소드는 스프링 데이터 JPA에서 제공하는 핵심 기능 중 하나로 Repository 인터페이스에 간단한 네이밍 룰을 이용하여 메소드 작성하면 원하는 쿼리 실행 가능
find + (엔티티 이름) + By + 변수이름

➡️ itemNm(상품명)으로 데이터를 조회하기 위해서 By 뒤에 필드명인 ItemNm을 메소드의 이름에 붙여줌. 엔티티명은 생략 가능하므로 findItemByItemNm 대신 findByItemNm으로 메소드명 만들어줌. 매개변수로는 검색할 때 사용할 상품명 변수를 넘겨줌.
➡️ 1) 테스트 코드 실행 시 데이터베이스에 상품 데이터가 없으므로 테스트 데이터 생성을 위해서 10개의 상품을 저장하는 메소드를 작성하여 findByItemNmTest()에서 실행.
➡️ 2) ItemRepository 인터페이스에 작성했던 findByItemNm 메소드를 호출함. 파라미터는 "테스트 상품1"이라는 상품명을 전달.
➡️ 3) 조회 결과 얻은 item 객체들을 출력


✏️ 쿼리 메소드 Sample 및 JPQL snippet

➡️ 상품을 상품명과 상품 상세 설명을 OR조건을 이용해 조회
➡️ 1) 기존에 만들었던 테스트 상품을 만드는 메소드를 실행하여 조회할 대상 만듦.
➡️ 2) 상품명이 "테스트 상품1" 또는 상품 상세 설명이 "테스트 상품 상세 설명5"이면 해당 상품을 itemList에 할당함. 테스트 코드를 실행하면 조건대로 2개의 상품이 출력됨 

➡️ 파라미로 넘어온 price 변수보다 값이 작은 상품 데이터를 조회
➡️ 현재 데이터베이스에 저장된 가격은 10001 ~ 10010. 테스트 코드 실행 시 10개의 상품을 저장하는 로그가 콘솔에 나타나고 맨 마지막에 가격이 10005보다 작은 4개의 상품을 출력함 
오름차순 : OrderBy + 속성명 + Asc
내림차순 : OrderBy + 속성명 + Desc


SQL : 데이터베이스의 테이블을 대상으로 쿼리 수행
JPQL : 엔티티 객체를 대상으로 쿼리 수행 (객체지향쿼리)
➡️ SQL을 추상화 해서 사용하기 때문에 특정 데이터베이스 SQL에 의존하지 않음. 즉, JPQL로 작성했다면 데이터베이스가 변경되어도 애플리케이션이 영향 받지 않음!

➡️ 1) @Query 안에 JPQL로 작성한 쿼리문을 넣어줌. from 뒤에는 엔티티 클래스로 작성한 Item을 지정해주고, Item으로부터 데이터를 select 하겠다는 의미
➡️ 2) 파라미터에 @Param 이용하여 파라미터로 넘어온 값을 JPQL에 들어갈 변수로 지정해줌. 현재는 itemDetail 변수를 "like % %" 사이에 ":itemDetail"로 값이 들어가도록 작성

기존의 데이터베이스에서 사용하던 쿼리를 그대로 사용해야 할 때는 @Query의 nativeQuery 속성을 사용하면 기존 쿼리를 그대로 활용 가능함 ➡️ 하지만 종속되는 쿼리문을 사용하기 때문에 독립적이라는 장점을 잃음


@Query 안에 JPQL 문법으로 문자열을 입력하기 때문에 잘못 입력하면 컴파일 시점에서 에러를 발견할 수 없다!
➡️ 이를 보완하는 방법으로 Querydsl 사용
Querydsl 장점
- 고정된 SQL문이 아닌 조건에 맞게 동적으로 쿼리를 생성 가능
- 비슷한 쿼리를 재사용할 수 있으며 제약 조건 조립 및 가독성을 향상시킬 수 있음
- 문자열이 아닌 자바 소스코드로 작성하기 때문에 컴파일 시점에 오류를 발견할 수 있음
- IDE의 도움을 받아서 자동 완성 기능을 이용할 수 있기 때문에 생산성을 향상시킬 수 있음
➡️ Querydsl 사용하기 위한 설정
(버전을 주석처리 해줬음! 스프링부트 버전이 달라서 메이븐 컴파일 수행이 안됐기 때문에)
// <dependencies></dependencies> 사이에 삽입
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>4.3.1</version>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>4.3.1</version>
</dependency>
➡️ QItem 클래스가 생성됨. Item 클래스의 모든 필드들에 대해서 사용 가능한 peration을 호출 메소드가 정의
// <plugins></plugins> 사이에 삽입
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>
target/generated-sources/java
</outputDirectory>
<processor>
com.querydsl.apt.jpa.JPAAnnotationProcessor
</processor>
</configuration>
</execution>
</executions>
</plugin>
➡️ 1) 영속성 컨텍스트를 사용하기 위해 @PersistenceContext 를 이용해 EntityManager 빈을 주입함
➡️ 2) JPAQueryFactory를 이용해 쿼리를 동적으로 생성. 생성자의 파라미터로는 EntityManager 객체를 넣어줌
➡️ 3) Querydsl을 통해 쿼리를 생성하기 위해 플러그인을 통해 자동으로 생성된 QItem 객체를 이용
➡️ 4) 자바 소스코드지만 SQL문과 비슷하게 소스 작성 가능
➡️ 5) JPAQuery 메소드 중 하나인 fetch를 이용해 쿼리 결과를 리스트로 반환. fetch() 메소드 실행 시점에 쿼리문이 실행됨.
✏️ JPAQuery 데이터 반환 메소드
메소드 기능 List< T> fetch() 조회 결과 리스트 반환 T fetchOne 조회 대상이 1건인 경우 제네릭으로 지정한 타입 반환 T fetchFirst() 조회 대상 중 1건만 반환 Long fetchCount() 조회 대상 개수 반환 QueryResult< T> fetchResults() 조회한 리스트와 전체 개수를 포함한 QueryResults 반환
Predicate : '이 조건이 맞다'고 판단하는 근거를 함수로 제공하는 것
➡️ Repository에 Predicate를 파라미터로 전달하기 위해서 QueryDslPredicateExecutor 인터페이스를 상속 받음
✏️ QueryDslPredicateExecutor 인터페이스 정의 메소드
메소드 기능 long count(Predicate) 조건에 맞는 데이터의 총 개수 반환
➡️ QueryDslPredicateExecutor 인터페이스 상속을 추가
✏️ QueryDslPredicateExecutor 인터페이스 정의 메소드
메소드 기능 long count(Predicate) 조건에 맞는 데이터의 총 개수 반환 boolean exists(Predicate) 조건에 맞는 데이터 존재 여부 반환 Iterable findAll(Predicate) 조건에 맞는 모든 데이터 반환 Page< T> findAll(Predicate, Pageable) 조건에 맞는 페이지 데이터 반환 Iterable findAll(Prediacte, Sort) 조건에 맞는 정렬된 데이터 반환 T findOne(Predicate) 조건에 맞는 데이터 1개 반환
➡️ 1) 상품 데이터를 만드는 새로운 메소드를 하나 만듦. 1번 ~ 5번 상품의 판매상태를 SELL 지정하고, 6번 ~ 10번 판매상태를 SOLD_OUT 세팅해서 생성
➡️ 2) BooleanBuilder는 쿼리에 들어갈 조건을 만들어주는 빌더. Predicate를 구현하고 있으며 메소드 체인 형식으로 사용 가능함
➡️ 3) 필요한 상품을 조회하는데 필요한 "and"조건을 추가하고 있음. 상품의 판매상태가 SELL일 때만 booleanBuilder에 판매상태 조건을 동적으로 추가하는 것을 볼 수 있음.
➡️ 4) 데이터를 페이징해 조회하도록 PageRequest.of() 메소드를 이용해 Pagealbe 객체를 생성. 첫번째 인자는 조회할 페이지의 번호, 두번째 인자는 한 페이지당 조회할 데이터의 개수를 넣어줌.
➡️ 5) QueryDslPredicateExecutor 인터페이스에서 정의한 findAll() 메소드를 이용해 조건에 맞는 데이터를 Page 객체로 받아옴