https://rogi221.tistory.com/167
Spring Boot - 쇼핑몰 프로젝트 02 (Spring Data JPA - 1)
JPA 1. JPA(Jave Persistence API’)는 자바 ORM 기술에 대한 API 표준 2. ORM이란 ‘Object Relational Mapping’의 약자로 객체와 관계형 데이터베이스 매핑 JPA 장점 특정 데이터베이스에 종속되지 않음 객체지향
rogi221.tistory.com
https://rogi221.tistory.com/168
Spring Boot - 쇼핑몰 프로젝트 02 (Spring Data JPA - 2)
https://rogi221.tistory.com/167 Spring Boot - 쇼핑몰 프로젝트 02 (Spring Data JPA - 1) JPA 1. JPA(Jave Persistence API’)는 자바 ORM 기술에 대한 API 표준 2. ORM이란 ‘Object Relational Mapping’의 약자로 객체와 관계형 데이
rogi221.tistory.com
Spring DATA JPA @Query 어노테이션
- 조건이 많아질 때 쿼리 메소드를 선언하면 이름이 길어져 오히려 보기 힘들다는 단점이 있음.
- @Query 어노테이션을 이용하면 SQL과 유사한 JPQL (Java Persistence Query Language) 이라는 객체지향 쿼리 언어를 통해 복잡한 쿼리도 처리 가능.
- JPQL은 엔티티 객체를 대상으로 쿼리를 수행하는 객체지향 쿼리.
- JPQL은 SQL을 추상화해서 사용하기 때문에 특정 데이터베이스 SQL에 의존하지 않음.
@Query를 이용한 검색 처리하기
ItemRepository.java
// ItemRepository.java
import com.shop.entity.Item;
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 ItemRepository extends JpaRepository<Item, Long> {
... 생략 ...
@Query("select i from Item i where i.itemDetail like %:itmeDetail% order by i.price desc")
List<Item> findByItemDetail(@Param("itemDetail") String itemDetail);
}
ItemRepositoryTest.java
// ItemRepositoryTest.java
... 생략 ...
@Test
@DisplayName("@Query를 이용한 상품 조회 테스트")
public void findByItemDetailTest(){
this.createItemList();
List<Item> itemList = itemRepository.findByItemDetail("테스트 상품 상세 설명");
for (Item item : itemList){
System.out.println(item.toString());
}
}
}
@Query-nativeQuery 속성 예제
- @Query의 nativeQuery 속성을 사용하면 기존 쿼리를 그대로 활용할 수 있음.
- 특정 데이터베이스에 종속되는 쿼리문을 사용하기 때문에 데이터베이스에 대해 독립적이라는 장점을 잃어버림.
- 기존에 작성한 통계용 쿼리처럼 복잡한 쿼리를 그대로 사용해야 하는 경우 활용 가능.
@Query-nativeQuery 속성 예제
ItemRepository.java
// ItemRepository.java
... 생략 ...
@Query(value="select * from item i where i.item_detail like " +
"%:itemDetail% order by i.price desc", nativeQuery = true)
List<Item> findByItemDetailByNative(@Param("itemDetail") String itemDetail);
}
ItemRepositoryTest.java
// ItemRepositoryTest.java
... 생략 ...
@Test
@DisplayName("nativeQuery 속성을 이용한 상품 조회 테스트")
public void findByItemDetailByNative(){
this.createItemList();
List<Item> itemList =
itemRepository.findByItemDetailByNative("테스트 상품 상세 설명");
for (Item item : itemList) {
System.out.println(item.toString());
}
}
}
Spring DATA JPA Querydsl
- Querydsl은 JPQL을 코드로 작성할 수 있도록 도와주는 빌더 API
- 문자열이 아닌 자바 소스코드로 작성하기 때문에 컴파일 시점에 오류를 발견할 수 있음
- 비슷한 쿼리를 재사용할 수 있으며 제약 조건 조립 및 가독성을 향상시킬 수 있음
- 고정된 SQL문이 아닌 조건에 맞게 동적으로 쿼리를 생성할 수 있음
- IDLE의 도움을 받아서 자동 완성 기능을 이용할 수 있기 때문에 생산성을 향상시킬 수 있음
- Querydsl을 사용하기 위한 의존성 추가
pom.xml
// pom.xml
... 생략 ...
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>5.0.0</version>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>5.0.0</version>
</dependency>
</dependencies>
... 생략 ...
<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>
</plugins>
</build>
</project>
- QDomain 임포트가 안 될 경우, target 폴더 아래의 generated-sources 폴더를 클릭 하여 <Sources> 버튼을 클릭해 소스코드로 인식할 수 있게 처리
QItem : QueryDSL에서 사용하는 QueryDSL 전용 엔티티
QueryDSL을 사용할 때 QueryDSL과 JPA를 같이 사용하는데 간단한 쿼리는 (Spring Data) JPA를 사용하고, 동적 쿼리는 QueryDSL을 사용하여 개발 한다.
의존성 추가 후 초반 Q 파일 생성하고 나서는 별도의 설정 없이 바로 사용가능
JPAQueryFactory를 이용한 상품 조회
ItemRepositoryTest.java
// ItemRepositoryTest.java
package com.shop.repository;
... 기존 임포트 생략 ...
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.querydsl.jpa.impl.JPAQuery;
import com.shop.entity.QItem;
import javax.persistence.Basic;
import javax.persistence.PersistenceContext;
import javax.persistence.EntityManager;
import java.time.LocalDateTime;
import java.util.List;
@SpringBootTest
@TestPropertySource(locations="classpath:application-test.properties")
class ItemRepositoryTest {
@PersistenceContext
EntityManager em;
... 생략 ...
@Test
@DisplayName("Querydsl 조회테스트1")
public void queryDslTest(){
this.createItemList();
JPAQueryFactory queryFactory = new JPAQueryFactory(em);
QItem qItem = QItem.item;
JPAQuery<Item> query = queryFactory.selectFrom(qItem)
.where(qItem.itemSellStatus.eq(ItemSellStatus.SELL))
.where(qItem.itemDetail.like("%" + "테스트 상품 상세 설명" + "%"))
.orderBy(qItem.price.desc());
List<Item> itemList = query.fetch();
for(Item item : itemList) {
System.out.println(item.toString());
}
}
}
QuerydslPredicateExecutor 상품 조회
Pageable
많은 게시판에서 모든 글들을 한번에 보여주지 않고 페이지를 나눠 쪽수별로 제공한다. 정렬 방식 또한 설정하여, 보고싶은 정보의 우선순위를 설정할 수 있다. 이처럼 정렬 방식과 페이지의 크기, 그리고 몇 번째 페이지인지를 요청에 따라 정보를 전달해 주는 것이 Pagination 이다.
이를 개발자가 직접 구현해서 사용할 수 있으나, JPA에서는 이를 편하게 사용할 수 있도록 Pageable이라는 객체를 제공한다.
QuerydslPredicateExecutor : Predicate란 '이 조건이 맞다'고 판단하는 근거를 함수로 제공하는 것
Repository에 Predicate를 파라미터로 전달하기 위해 QuerydslPredicateExecutor 인터페이스를 상속 받는다
BooleanBuilder : 쿼리의 조건 설정인 where 뒤의 조건을 생성해준다.
StringUtils 클래스 : 자바의 String 클래스가 제공하는 문자열 관련 기능을 강화한 클래스
PageRequest.of(page,size) : 페이지 0 부터 사이즈 몇개 출력
ItemRepository.java
// ItemRepository.java
package com.shop.repository;
... 기존임포트 생략 ...
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
public interface ItemRepository extends JpaRepository<Item, Long>, QuerydslPredicateExecutor<Item> {
... 생략 ...
}
ItemRepositoryTest.java
// ItemRepositoryTest.java
import com.querydsl.core.BooleanBuilder;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
... 기존 임포트 생략 ...
@SpringBootTest
@TestPropertySource(locations="classpath:application-test.properties")
class ItemRepositoryTest {
... 생략 ...
public void createItemList2() {
for(int i=1; i<=5; i++){
Item item = new Item();
item.setItemNm("테스트 상품" + i);
item.setPrice(10000 + i);
item.setItemDetail("테스트 상품 상세 설명" + i);
item.setItemSellStatus(ItemSellStatus.SELL);
item.setStockNumber(100);
item.setRegTime(LocalDateTime.now());
item.setUpdateTime(LocalDateTime.now());
itemRepository.save(item);
}
for(int i=6; i<=10; i++){
Item item = new Item();
item.setItemNm("테스트 상품" + i);
item.setPrice(10000 + i);
item.setItemDetail("테스트 상품 상세 설명" + i);
item.setItemSellStatus(ItemSellStatus.SELL);
item.setStockNumber(100);
item.setRegTime(LocalDateTime.now());
item.setUpdateTime(LocalDateTime.now());
itemRepository.save(item);
}
}
@Test
@DisplayName("상품 Querydsl 조회테스트 2")
public void queryDslTest2(){
this.createItemList2();
BooleanBuilder booleanBuilder = new BooleanBuilder();
QItem item = QItem.item;
String itemDetail = "테스트 상품 상세 설명";
int price = 10003;
String itemSellStat = "SELL";
booleanBuilder.and(item.itemDetail.like("%" + itemDetail + "%"));
booleanBuilder.and(item.price.gt(price));
if(StringUtils.equals(itemSellStat, ItemSellStatus.SELL)){
booleanBuilder.and(item.itemSellStatus.eq(ItemSellStatus.SELL));
}
Pageable pageable = PageRequest.of(0, 5);
Page<Item> itemPagingResult =
itemRepository.findAll(booleanBuilder, pageable);
System.out.println("total elements : " + itemPagingResult. getTotalElements());
List<Item> resultItemList = itemPagingResult.getContent();
for(Item resultItem: resultItemList) {
System.out.println(resultItem.toString());
}
}
}
'Spring boot & JPA' 카테고리의 다른 글
Spring Boot - 쇼핑몰 프로젝트 03 (Thymeleaf 학습하기 - 2) (0) | 2023.04.04 |
---|---|
Spring Boot - 쇼핑몰 프로젝트 03 (Thymeleaf 학습하기 - 1) (0) | 2023.04.04 |
Spring Boot - 쇼핑몰 프로젝트 02 (Spring Data JPA - 2) (0) | 2023.04.03 |
Spring Boot - 쇼핑몰 프로젝트 02 (Spring Data JPA - 1) (0) | 2023.04.03 |
Spring Boot - 쇼핑몰 프로젝트 01 (개발 환경 구축) (0) | 2023.04.03 |