Spring boot & JPA

Spring Boot - 쇼핑몰 프로젝트 02 (Spring Data JPA - 3)

록's 2023. 4. 3. 17:35
728x90
반응형

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());
        }
    }
}

728x90
반응형