728x90
package io.shi.dao.dao.hibernate;
import com.querydsl.jpa.impl.JPAQueryFactory;
import io.shi.dao.global.entity.Items;
import io.shi.dao.global.entity.OrderItems;
import io.shi.dao.global.entity.Orders;
import jakarta.persistence.EntityManager;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;
import static io.jieun.dao.global.entity.QItems.items;
@Slf4j
@Repository
@Transactional
@RequiredArgsConstructor
public class HibernateOrderRepository {
//oderitems랑 order을 이곳에서 같이 관리하도록 하겠다.
private final EntityManager entityManager;
public Orders saveOrder(Orders orders) {
entityManager.persist(orders);
return orders;
}
//JPQL -> order를 orderCode로 찾아 올 예정
//orderCode 유니크
//Optional
public Optional<Orders> findOrderByOrderCode(String orderCode) {
return entityManager.createQuery("select o from Orders o where o.orderCode = :orderCode", Orders.class)
.setParameter("orderCode", orderCode)
.getResultList()
.stream()
.findAny();
}
public Long removeOrderByOrderCode(String orderCode) {
Optional<Orders> orderOptional = findOrderByOrderCode(orderCode);
// if (orderOptional.isEmpty()) {
// throw new NoSuchElementException();
// }
//
// Orders findOrder = orderOptional.get();
Orders findOrder = orderOptional.orElseThrow(() -> new NoSuchElementException());
entityManager.remove(findOrder);
return findOrder.getId();
}
public OrderItems saveOrderItems(OrderItems orderItems) {
entityManager.persist(orderItems);
return orderItems;
}
public OrderItems getOrderItemsById(Long id) {
return entityManager.find(OrderItems.class, id);
}
public List<OrderItems> saveAllOrderItems(List<OrderItems> orderItems) {
int batchSize = 50;
for ( int i = 0; i < orderItems.size(); i++ ) {
entityManager.persist(orderItems.get(i));
if ( i % batchSize == 0 && i > 0 ) {
entityManager.flush();
entityManager.clear();
}
}
entityManager.flush();
entityManager.clear();
return orderItems;
}
}
package io.shi.dao.dao.hibernate;
import io.shi.dao.global.entity.Items;
import io.shi.dao.global.entity.OrderItems;
import io.shi.dao.global.entity.Orders;
import io.shi.dao.util.TestUtils;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.transaction.Transactional;
import lombok.extern.slf4j.Slf4j;
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 java.util.List;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@Slf4j
@Transactional
@SpringBootTest
class HibernateOrderRepositoryTests {
@Autowired
HibernateOrderRepository repository;
@PersistenceContext
EntityManager entityManager;
@Test
@DisplayName("주문 저장 테스트")
void save_order_test() throws Exception {
String orderCode = TestUtils.genRandomOrderCode();
Orders order = Orders.builder()
.orderCode(orderCode)
.build();
Orders saved = repository.saveOrder(order);
assertThat(saved.getId()).isNotNull();
assertThat(saved.getOrderCode()).isEqualTo(orderCode);
assertThat(saved.getOrderCode()).isEqualTo(order.getOrderCode());
}
@Test
@DisplayName("주문 저장 오류 테스트")
void save_order_test_ng() throws Exception {
Orders order = Orders.builder()
.build();
assertThatThrownBy(
() -> {
repository.saveOrder(order);
}
).isInstanceOf(Exception.class);
}
@Test
@DisplayName("주문 코드로 주문 조회")
void find_order_by_order_code_test() throws Exception {
List<Items> items = TestUtils.generateItems(5);
for (Items item : items) {
entityManager.persist(item);
}
Orders order = Orders.builder()
.orderCode(TestUtils.genRandomOrderCode())
.build();
repository.saveOrder(order);
List<OrderItems> orderItems = TestUtils.genOrderItems(order, items, 5);
repository.saveAllOrderItems(orderItems);
Optional<Orders> orderOptional = repository.findOrderByOrderCode(order.getOrderCode());
assertThat(orderOptional.isPresent()).isTrue();
Orders findOrder = orderOptional.get();
assertThat(findOrder.getId()).isNotNull();
findOrder.getItems()
.forEach(
i -> {
log.info("{} = {}" ,i.getItems().getName(), i.getQuantity());
}
);
}
}
✅ HibernateOrderRepository 클래스
@Repository
@Transactional
@RequiredArgsConstructor
public class HibernateOrderRepository {
- @Repository: 스프링이 이 클래스를 빈으로 등록하고 예외를 감싸서 처리할 수 있게 함.
- @Transactional: 클래스 단위로 붙으면 모든 public 메서드가 트랜잭션 안에서 실행됨.
- @RequiredArgsConstructor: final 필드인 entityManager를 자동으로 주입받게 해줌 (생성자 자동 생성)
필드 선언
private final EntityManager entityManager;
- JPA의 핵심 객체로, DB와 직접 소통함 (엔티티 저장/조회/수정/삭제)
- 스프링이 자동으로 주입해줌
주문 저장
public Orders saveOrder(Orders orders) {
entityManager.persist(orders);
return orders;
}
- persist()는 해당 객체를 영속 상태로 만들어줌 (즉, DB와 동기화 준비 완료)
- Orders 객체를 저장하면 DB에 insert가 발생함 (flush 시점 기준)
주문 조회
public Optional<Orders> findOrderByOrderCode(String orderCode) {
return entityManager.createQuery("select o from Orders o where o.orderCode = :orderCode", Orders.class)
.setParameter("orderCode", orderCode)
.getResultList()
.stream()
.findAny();
}
- JPQL을 이용해 orderCode로 주문을 조회
- getSingleResult()는 결과 없으면 예외, getResultList()는 빈 리스트 → 안전하게 stream으로 Optional 반환
주문 삭제
public Long removeOrderByOrderCode(String orderCode) {
Optional<Orders> orderOptional = findOrderByOrderCode(orderCode);
Orders findOrder = orderOptional.orElseThrow(() -> new NoSuchElementException());
entityManager.remove(findOrder);
return findOrder.getId();
}
- orderCode로 주문을 찾고 없으면 예외 발생
- entityManager.remove()를 통해 해당 객체 삭제
- 삭제된 객체의 ID 반환
주문 아이템 저장
public OrderItems saveOrderItems(OrderItems orderItems) {
entityManager.persist(orderItems);
return orderItems;
}
- 단건 저장. persist()는 DB와 동기화 준비
주문 아이템 ID로 조회
public OrderItems getOrderItemsById(Long id) {
return entityManager.find(OrderItems.class, id);
}
- find()는 기본키(PK)로 엔티티 조회
- 없으면 null 반환
주문 아이템 여러 개 저장 (배치 처리)
public List<OrderItems> saveAllOrderItems(List<OrderItems> orderItems) {
int batchSize = 50;
for ( int i = 0; i < orderItems.size(); i++ ) {
entityManager.persist(orderItems.get(i));
if ( i % batchSize == 0 && i > 0 ) {
entityManager.flush();
entityManager.clear();
}
}
entityManager.flush();
entityManager.clear();
return orderItems;
}
flush()와 clear() 설명
- flush(): 영속성 컨텍스트의 변경 사항을 DB에 강제로 반영
- 즉, 지금까지 persist() 해둔 내용들을 진짜 DB에 insert 함
- clear(): 영속성 컨텍스트를 초기화 (detach)
- 1차 캐시 비움. 메모리 부담 줄임
- 이걸 배치 저장에 사용하는 이유는:
- 메모리 절약: 한번에 너무 많은 객체를 영속 상태로 두면 메모리 터짐
- 성능 향상: 일정 간격으로 flush 하고 clear 하면 성능 최적화
✅ HibernateOrderRepositoryTests 테스트 클래스 설명
주문 저장 테스트
@Test
@DisplayName("주문 저장 테스트")
void save_order_test()
- 랜덤 주문 코드를 가진 Orders 객체 생성
- 저장 → 반환된 객체의 ID, orderCode가 올바르게 들어갔는지 검증
주문 저장 실패 테스트
@Test
@DisplayName("주문 저장 오류 테스트")
void save_order_test_ng()
- 필수 값 없이 Orders 객체 저장 시 예외 발생
- 스프링 JPA는 기본적으로 DDL 제약조건 따라 예외 던짐
주문 코드로 조회 테스트
@Test
@DisplayName("주문 코드로 주문 조회")
void find_order_by_order_code_test()
1. 아이템 5개 생성 후 DB 저장
List<Items> items = TestUtils.generateItems(5);
for (Items item : items) {
entityManager.persist(item);
}
2. 주문 객체 생성 및 저장
Orders order = Orders.builder().orderCode(랜덤코드).build();
repository.saveOrder(order);
3. 주문과 연결된 OrderItems 생성 및 저장
List<OrderItems> orderItems = TestUtils.genOrderItems(order, items, 5);
repository.saveAllOrderItems(orderItems);
4. 주문 조회 후 아이템 정보 로그 출력
Optional<Orders> orderOptional = repository.findOrderByOrderCode(order.getOrderCode());
Orders findOrder = orderOptional.get();
findOrder.getItems().forEach(
i -> {
log.info("{} = {}", i.getItems().getName(), i.getQuantity());
}
);
- 주문 → 주문 아이템 연관관계 매핑 확인
정리
| 항목 | 설명 |
| EntityManager.persist() | 객체를 영속 상태로 만듬 (DB에 insert 준비) |
| flush() | DB에 강제로 insert 실행 (트랜잭션 내에서) |
| clear() | 영속성 컨텍스트 초기화 (메모리 절약) |
| find() | PK로 객체 조회 (1차 캐시 포함) |
| JPQL | 객체지향 쿼리 (SQL과 다름) |
| @Transactional | 메서드/클래스 단위 트랜잭션 관리 |
| Optional | 값이 있을 수도, 없을 수도 있을 때 안전하게 처리 |
728x90
'프로그래밍 > Spring' 카테고리의 다른 글
| JPA 핵심 메서드 정리 (0) | 2025.04.14 |
|---|---|
| entityManager.persist()란? (0) | 2025.04.14 |
| Hibernate실습, JPQL 활용 - 4월 14일 (0) | 2025.04.14 |
| Hibernate란 - 4월 11일 (0) | 2025.04.11 |
| JPA- 4월 10일 (0) | 2025.04.10 |