티스토리 뷰
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)를 이해하는 것이 가장 중요
- 엔티티를 영구 저장하는 환경으로 엔티티 매니저를 통해 영속성 컨텍스트에 접근
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
}
-
- : itemNum(상품명)으로 데이터를 조회하기 위해서 By 뒤에 필드명인 itemNum을 메소드의 이름에 붙여 준다.
엔티티명은 생략이 가능하므로 findItemByItemNum으로 메소드명을 만들어 줍니다. 매개 변수로는 검색할 때
사용할 상품명 변수를 넘겨줍니다.
- : itemNum(상품명)으로 데이터를 조회하기 위해서 By 뒤에 필드명인 itemNum을 메소드의 이름에 붙여 준다.
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추가
- : 테스트 코드 실행 시 데이터베이스에 상품 데이터가 없으므로 테스트 데이터 생성을 위해서 10개의 상품을
저장하는 메소드를 작성하여 findByItemNumTest()에서 실행해 줍니다. - : ItemRepository 인터페이스에 작성했던 findByItemNum 메소드를 호출합니다. 파라미터로는 "테스트 상품1"
이라는 상품명을 전달하겠습니다. - : 조회 결과 얻은 item 객체들을 출력합니다.
- : 테스트 코드 실행 시 데이터베이스에 상품 데이터가 없으므로 테스트 데이터 생성을 위해서 10개의 상품을
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" 또는 상세 설명이 "테스트 상품 상세 설명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());
}
}
-
- : 현재 데이터베이스에 저장된 가격은 10001 ~ 10010입니다. 테스트 코드 실행 시 10개의 상품을 저장하는
로그가 콘솔에 나타나고 맨 마지막에 가격이 10005보다 작은 4개의 상품을 출력해주는 것을 확인 할 수
있습니다.
- : 현재 데이터베이스에 저장된 가격은 10001 ~ 10010입니다. 테스트 코드 실행 시 10개의 상품을 저장하는
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
'스프링 입문 - 코드로 배우는 스프링부트 웹 프로젝트' 카테고리의 다른 글
인텔리제이로 스프링 프레임워크 프로젝트 만들기 - 5장 - (0) | 2023.04.05 |
---|---|
인텔리제이로 스프링 프레임워크 프로젝트 만들기 - 3장 - 예제 4부터 136 p 까지 저장 (0) | 2023.04.04 |
인텔리제이로 스프링 프레임워크 프로젝트 만들기 - 3장 - 예제 3까지 (0) | 2023.04.04 |
스프링 부트 쇼핑몰 프로젝트 - 개발 환경 구축 (0) | 2023.04.03 |
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- 다른데서 react
- react 시작 오류
- ID
- 다중체크박스 처리
- 제약조건
- mircrosoft visual studio
- @Builder
- C언어
- @reqeustBody
- 받아오기
- findFirstBy
- reactStart
- 셀프로젝트
- SCP
- selectbox
- App
- th:selected
- CheckBox
- C
- 셀렉트박스
- 씹어먹는 C 언어
- 체크박스
- @RequestParam
- optional
- JPA
- reactApp
- 서버전송
- 아이디
- commit 에러
- React
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
글 보관함