티스토리 뷰

728x90

Shop 데이터베이스
create database shop default character set utf8 collate utf8_general_ci;

YDVYGOStd13

엔티티 : 데이터베이스의 테이블에 대응하는 클래스
@Entity가 붙은 클래스 JPA에서 관리하며 엔티티라 한다
itme.java 클래스를 만들어서 @Entity 어노테이션을 붙이면 이 클래스가 엔티티가 된다

엔티티 매니저 팩토리 (Entity Manager Factory) : 엔티티 매니저 인스턴스를 관리하는 주체
애플리케이션 실행 시 한개만 만들어지며 사용자로 부터 요청이 오면 엔티티 매니저 팩토리로부터 엔티티 매니저를 생성한다

엔티티 매니저 (Entity Manager)
  • 영속성 컨텍스트에 접근하여 엔티티에 대한 데이터베이스 작업을 제공
  • 내부적으로 데이터베이스 커넥션을 사용해서 데이터베이스 접근

엔티티 생명주기

엔테티 생명 주기의 세부 내용
  • 비영속(new) : new 키워드를 통해 생성된 상태로 영속성 컨텍스트와 관련 없는 상태
  • 영속(managed)
    • 엔티티가 영속성 컨텍스트에 저장된 상태로 영속성 컨텍스트에 의해 관리되는 상태
    • 영속 상태에서 데이터베이스에 저장되지 않으며, 트랜잭션 커밋 시점에 데이터베이스 반영
  • 준영속 상태(detached) : 영속성 컨텍스트에 엔티티가 저장되었다가 분리된 상태
  • 삭제 상태(removed) : 영속성 컨텍스트와 데이터베이스에서 삭제된 상태
엔티티 매니저의 몇 가지 메소드
  • find() 메소드 : 영속성 컨텍스트에서 엔티티를 검색하고 영속성 컨텍스트에 없을 경우 데이터베이스에서
    데이터를 찾아 영속성 컨텍스에 저장
  • persist() 메소드 : 엔티티를 영속성 컨텍스트에 저장
  • remove() 메소드 : 엔티티 클래스를 영속성 컨텍스트에서 삭제
  • flush() 메소드 : 영속성 컨텍스트에 저장된 내용을 데이터베이스에 반영
영속성 컨텍스트
  • JPA를 이해하기 위해서는 영속성 컨텍스트(Persistence Context)를 이해하는 것이 가장 중요
  • 엔티티를 영구 저장하는 환경으로 엔티티 매니저를 통해 영속성 컨텍스트에 접근

JPA 동작 방식

ItemSellStatus enum 생성
package com.shopping.project.constant;

public enum ItemSellStatus {
    SELL, SOLD_OUT
}
Item Class @Entity Mapping
package com.shopping.project.entity;


import com.shopping.project.constant.ItemSellStatus;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import javax.persistence.*;
import java.time.LocalDateTime;



@Entity----------------------------------  ①
@Table(name="itme")----------------------  ①
@Getter
@Setter
@ToString
public class Item {

    @Id                                    ----------------------------------------     >   ②
    @Column(name="item_id")                ----------------------------------------     >   ②
    @GeneratedValue(strategy = GenerationType.AUTO)    ----------------------------     >   ②
    private Long id;        //상품 코드

    @Column(nullable = false, length = 50)            ----------------------- ③
    private String itemNum; // 상품명

    @Column(name="price", nullable = false)
    private int price;      // 상품 가격

    @Column(nullable = false)
    private int stockNumber;    // 재고 수량

    @Lob
    @Column(nullable = false)
    private String itemDetail;  //상품 상세 설명

    @Enumerated(EnumType.STRING)
    private ItemSellStatus itemSellStatus;  //상품 판매 상태

    private LocalDateTime regTime;      // 등록 시간

    private LocalDateTime updateTime;   // 수정시간
}
  • ① : Item Class를 entity로 선언합니다. 또한 @Table Annotation을 통해 어떤 테이블과 매핑될지를 지정합니다. item 테이블과 매핑되도록 name을 item으로 지정합니다.
  • ② : entity로 선언한 클래스는 반드시 기본키를 가져야 합니다. 기본키가 되는 멤버변수에 @Id 어노테이션을 붙여줍니다. 그리고 테이블에 매핑될 컬럼의 이름을 @Column 어노테이션을 통해 설정해줍니다. Item 클래스의 id 변수와 item 테이블의 item_id 컬럼이 매핑되도록 합니다. 마지막으로 @GeneratedValue 어노테이션을 통해 기본키 생성 전략을 AUTO로 지정합니다.
  • ③ : @Coulumn 어노테이션의 nullable 속성을 이용해서 항상 값이 있어야 하는 필드는 not null 설정을 합니다. String필드는 default 값으로 255가 설정돼 있습니다. 각 String 필드마다 필요한 길이를 length 속성에 default 값을 세팅합니다.
실행확인

MySQL 확인

Repository 설계하기

상품 Repository 작성 및 테스트 하기

temRepository interface 작성
package com.shopping.project.repository;

import com.shopping.project.entity.Item;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ItemRepository extends JpaRepository<Item, Long> {
}
  • JpaRepository를 상속받는 ItemRepository 작성
  • JpaRepository는 2개의 제네릭 타입을 사용 하는데 첫 번째에는 엔티티 타입 클래스, 두 번째는 기본키 타입을 넣어 준다.
  • Item 클래스는 기본키 타입이 Long이므로 Long을 넣어준다. JpaRepository는 기본적인 CRUD 및 페이징 처리를 위한 메소드가 정의돼 있다
  • 메소드 몇 가지를 살펴 보면 엔티티를 저장, 삭제 또는 엔티티의 개수 출력 등의 메소드를 볼 수 있다.
JpaRepository에서 지원하는 메소드 예시
  • save(S entity) - 엔티티 저장 및 수정
  • void delete(T entity) - 엔티티 삭제
  • count() - 엔티티 총 개수 반환
  • Iterable findAll() - 모든 엔티티 조회
h2 DB 사용 위해 resources -> application-test.properties 생성
#Datasources 설정
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:test
spring.datasource.username=sa
spring.datasource.password=

# H2 데이터베이스 방언 설정
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
ItemRepositoryTest 작성
package com.shopping.project.repository;

import com.shopping.project.constant.ItemSellStatus;
import com.shopping.project.entity.Item;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;

import java.time.LocalDateTime;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest  ------------------------①
@TestPropertySource(locations = "classpath:application-test.properties")  ---②
class ItemRepositoryTest {

    @Autowired
    ItemRepository itemRepository;        -------------------③

    @Test        -------------------------------- ④
    @DisplayName("상품 저장 테스트")    ---------- ⑤
    public void createItemTest(){
        Item item = new Item();
        item.setItemNum("테스트 상품");
        item.setPrice(10000);
        item.setItemDetail("테스트 상품 상세 설명");
        item.setItemSellStatus(ItemSellStatus.SELL);
        item.setStockNumber(100);
        item.setRegTime(LocalDateTime.now());
        item.setUpdateTime(LocalDateTime.now());
        Item saveItem = itemRepository.save(item);
        System.out.println(saveItem.toString());
    }

}
  • ① : 통합 테스트를 위해 스프링 부트에서 제공하는 어노테이션 입니다. 실제 애플리케이션을 구동할 때 처럼
    모든 Bean을 IoC 컨테이너에 등록합니다. 애플리케이션의 규모가 크면 속도가 느려 질 수 있음
  • ② : 테스트 코드 실행 시 application.properties에 설정해둔 값보다 application-test.properties에 같은 설정이
    있다면 더 높은 우선순위를 부여합니다. 기존에는 MySQL을 사용했지만 테스트 코드 실행 시에는 H2 데이터베이스를
    사용하게 됩니다.
  • ③ : ItemRepository를 사용하기 위해서 @Autowired 어노테이션을 이용하여 Bean을 주입합니다.
  • ④ : 테스트할 메소드 위에 선언하여 해당 메소드를 테스트 대상으로 지정합니다.
  • ⑤ : Junit5에 추가된 어노테이션으로 테스트 코드 실행 시 @DisplayName에 지정한 테스트명이 노출됩니다.

2.5 쿼리 메소드

쿼리 메소드를 이용한 상품 조회하기

ItemRepository에 findByItemNum 메소드를 추가
package com.shopping.project.repository;

import com.shopping.project.entity.Item;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface ItemRepository extends JpaRepository<Item, Long> {

    List<Item> findByItemNum(String itemNm); -------------------------- 1

}
    1. : itemNum(상품명)으로 데이터를 조회하기 위해서 By 뒤에 필드명인 itemNum을 메소드의 이름에 붙여 준다.
      엔티티명은 생략이 가능하므로 findItemByItemNum으로 메소드명을 만들어 줍니다. 매개 변수로는 검색할 때
      사용할 상품명 변수를 넘겨줍니다.
ItemRepositoryTest 클래스에 테스트 코드를 추가
    public void createItemList(){         -------------------- 1.
        for(int i=1; i<=10; i++){
            Item item = new Item();
            item.setItemNum("테스트 상품" + i);
            item.setPrice(10000 + i);
            item.setItemDetail("테스트 상품 상세 설명" + i);
            item.setItemSellStatus(ItemSellStatus.SELL);
            item.setStockNumber(100);
            item.setRegTime(LocalDateTime.now());
            item.setUpdateTime(LocalDateTime.now());
            Item saveItem = itemRepository.save(item);
        }
    }


    @Test
    @DisplayName("상품 조회 테스트")
    public void findByItemNumTest(){
        this.createItemList();
        List<Item> itemList = itemRepository.findByItemNum("테스트상품1");    --- 2.
        for(Item item : itemList){
            System.out.println(item.toString());        ----------------------- 3.
        }
    }
  • createItemList(), findByItemNumTest추가
    1. : 테스트 코드 실행 시 데이터베이스에 상품 데이터가 없으므로 테스트 데이터 생성을 위해서 10개의 상품을
      저장하는 메소드를 작성하여 findByItemNumTest()에서 실행해 줍니다.
    2. : ItemRepository 인터페이스에 작성했던 findByItemNum 메소드를 호출합니다. 파라미터로는 "테스트 상품1"
      이라는 상품명을 전달하겠습니다.
    3. : 조회 결과 얻은 item 객체들을 출력합니다.

OR 조건 처리기

ItemRepository interface에 코드 추가
List<Item> findByItemNmOrItemDetail(String itemNum, String itemDetail);                -- 1
  • 1. : 상품을 상품명과 상품 상세 설명을 OR 조건을 이용하여 조회하는 쿼리 메소드입니다.
ItemRepositoryTest에 코드 추가
    @Test
    @DisplayName("상품명, 상품상세설명 or 테스트")
    public void findByItemNumOrItemDetailTest(){
        this.createItemList();                           ----------------- 1.
        List<Item> itemList = itemRepository.findByItemNmOrItemDetail("테스트 상품1", "테스트 상품 상세설명 5");   2.
        for(Item item : itemList){
            System.out.println(item.toString());
        }

    }
  • findByItemNumOrItemDetailTest 메소드 추가
    1. : 기존에 만들었던 테스트 상품을 만드는 메소드를 실행하여 조회할 대상을 만들어 주겠습니다.
    2. : 상품명이 "테스트 상품1" 또는 상세 설명이 "테스트 상품 상세 설명5" 이면 해당 상품을 itemList에 할당합니다. 테스트 코드를 실행하면 조건대로 2개의 상품이 출력되는 것을 볼 수 있습니다.

LessThan 조건 처리하기

ItemRepository interface에 코드 추가
List<Item> findByPriceLessThan(Integer price);     ----------- 1.
  • 1. : 파라미터로 넘어온 price 변수보다 값이 작은 상품 데이터를 조회하는 쿼리 메소드 입니다,
ItemRepositoryTest에 코드 추가
    @Test
    @DisplayName("가격 LessThan 테스트")
    public void findByPriceLessThanTest(){    // findByPriceLessThan << 쿼리문
        this.createItemList();
        List<Item> itemList = itemRepository.findByPriceLessThan(10005);   -- 1.
        for(Item item : itemList){
            System.out.println(item.toString());
        }
    }
    1. : 현재 데이터베이스에 저장된 가격은 10001 ~ 10010입니다. 테스트 코드 실행 시 10개의 상품을 저장하는
      로그가 콘솔에 나타나고 맨 마지막에 가격이 10005보다 작은 4개의 상품을 출력해주는 것을 확인 할 수
      있습니다.

OrderBy로 정렬 처리하기

ItemRepository interface에 코드 추가
    List<Item> findByPriceLessThanOrderByPriceDesc(Integer price);
ItemRepositoryTest에 코드 추가
    @Test
    @DisplayName("가격 내림차순 ")
    public void findByPriceLessThanOrderByPriceDesc(){
        this.createItemList();
        List<Item> itemList = itemRepository.findByPriceLessThanOrderByPriceDesc(10005);
        for(Item item : itemList){
            System.out.println(item.toString());
        }
    }

2.6 Spring Data JPA @Query Annotation

@Query를 이용한 검색 처리하기

ItemRepository interface에 코드 추가
    @Query("select i from Item i where i.itemDetail like %:itemDetail% order by i.price desc")
    List<Item> findByItemDetail(@Param("itemDetail") String itemDetail);
ItemRepositoryTest에 코드 추가
   @Test
   @DisplayName("@Query를 이용한 상품조회 테스트")
    public void findByItemDetailTest(){
        this.createItemList();
        List<Item> itemList = itemRepository.findByItemDetail("테스트 상품 상세 설명");
        for(Item item : itemList){
            System.out.println(item.toString());
        }
   }

@Query-nativeQuer 속성 예제

ItemRepository interface에 코드 추가
    @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에 코드 추가
   @Test
    @DisplayName("nativeQuery 속성을 이용한 상품 조회 테스트")
    public void findByItemDetailByNative(){
        this.createItemList();
        List<Item> itemList =  itemRepository.findByItemDetailByNative("테스트 상품 상세 설명");
        for (Item item : itemList){
            System.out.println(item.toString());
        }
   }

QItem : QueryDSL에서 사용하는 QueryDSL 전용 엔티티

  • QueryDSL을 사용할 때 QueryDSL과 JPA를 같이 사용하는데 간단한 쿼리는 (Spring Data) JPA를 사용하고, 동적 쿼리는 QueryDSL을 사용하여 개발
  • 의존성 추가 후 초반 Q 파일 생성하고 나서는 별도의 설정 없이 바로 사용가능

JPAQueryFactory를 이용한 상품 조회 예제

ItemRepositoryTest에 코드추가
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.shopping.project.entity.QItem;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;




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

        }
    }

Pageable
많은 게시판에서 모든 글들을 한번에 보여주지 않고 페이지를 나눠 쪽수별로 제공한다
정렬 방식 또한 설정하여, 보고 싶은 정보의 우선순위를 설정할 수 있다.
이처럼 정렬 방식과 페이지의 크기, 그리고 몇 번째 페이지인지를 요청에 따라 정보를
전달해 주는 것이 Pagination 이다.

이를 개발자가 직접 구현해서 사용할 수 있으나, JPA에서는 이를 편하게 사용할 수 있도록
Pageable이라는 객체를 제공한다.

QueryDslPredicateExecutor : Predicate란 '이 조건이 맞다'고 판단하는 근거를 함수로 제공하는 것
Repository에 Predicate를 파라미터로 전달하기 위해 QueryDslPredicateExecutor 인터페이스를 상속 받는다

728x90
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함