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