Engineering Note

[JPA] JPA save 쿼리메소드 실행시 내부 동작 흐름 정리 본문

Server/JPA ORM

[JPA] JPA save 쿼리메소드 실행시 내부 동작 흐름 정리

Software Engineer Kim 2025. 9. 14. 13:06

 

 

내부 동작 흐름 정리 상황 설명 

 

Order 엔티티가 있고, Order와 일대다 관계를 맺는 orderItem 엔티티를 cascde.all로 설정 해두었다. 이 상태에서 orderRepository.save(order)메서드를 정리하려고 한다.

package com.shop.entity;

import com.shop.contant.OrderStatus;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name = "orders")
@Getter
@Setter
public class Order extends BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "order_id")
    private Long id;

    @ManyToOne
    @JoinColumn(name = "member_id")
    private Member member;

    private LocalDateTime orderDate; //주문일

    @Enumerated(EnumType.STRING)
    private OrderStatus orderStatus; //주문상태

    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
    private List<OrderItem> orderItems = new ArrayList<>();
}

 

 

1. orderRepository.save(order) 호출 시

1-1 Order 객체가 영속화

- Order 객체가 영속화 된다. 영속화의 의미는 JPA가 관리하는 메모리상의 객체를 관리하는 공간인 영속성 컨텍스트에 저장한다는 의미이다.

- 아직 DB에는 반영되지 않은 상태다.

 

1-2 Cascade 처리

- CascadeType.All 때문에, order.getOrderItems().add(orderItem)을 호출 했다면, order.getOrderItem() 안의 모든 OrderItem 객체도 모두 영속성 컨텍스트에 등록된다.

- 즉 부모 엔티티에 대한 상태 변화가 자식 엔티티에도 전파된다.

 

1-3 SQL 생성준비

- JPA는 SQL을 SQL 쓰기 지연 저장소에 저장한다.

- 아직 DB에는 save() 메서드에 해당하는 insert 쿼리문을 전송하지 않은 상태다.

 

 

2. 트랜잭션 종료시(@Transaction이 끝나거나 or flush() 호출시)

2-1 영속성 컨텍스트 객체를 DB에 반영

- 영속성 컨텍스트에 등록된 Order와 OrderItem 엔티티를 순서대로 DB에 insert 한다. 

 

 

정리

DB 반영 시점은 flush/커밋 시점이고, save() 쿼리메서드 만으로는 지연 SQL 저장소에 쿼리가 쌓이는 구조이고, DB에 즉시 반영되지는 않는다.

DB에 바로 반영하고 싶다면 saveAndFlush() 쿼리메서드를 사용하면 된다.

 

 

실습 코드

package com.shop.entity;

import com.shop.contant.ItemSellStatus;
import com.shop.repository.ItemRepository;
import com.shop.repository.OrderRepository;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityNotFoundException;
import jakarta.persistence.PersistenceContext;
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.ActiveProfiles;
import org.springframework.transaction.annotation.Transactional;

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

@SpringBootTest
@ActiveProfiles("test")
@Transactional
class OrderTest {
    @Autowired
    private OrderRepository orderRepository;

    @Autowired
    private ItemRepository itemRepository;

    @PersistenceContext
    private EntityManager em;

    public Item createItem() {
        Item item = new Item();
        item.setItemNm("테스트 상품");
        item.setPrice(10000);
        item.setItemDetail("상세설명");
        item.setItemSellStatus(ItemSellStatus.SELL);
        item.setStockNumber(100);
        return item;
    }

    @Test
    @DisplayName("영속성 전이 테스트")
    public void cascadeTest() {
        Order order = new Order();

        for(int i = 0; i < 3; i++) {
            Item item = this.createItem();
            itemRepository.save(item);
            OrderItem orderItem = new OrderItem();
            orderItem.setItem(item);
            orderItem.setCount(10);
            orderItem.setOrderPrice(item.getPrice());
            orderItem.setOrder(order);
            order.getOrderItems().add(orderItem);

        }
        System.out.println("save order");
        orderRepository.saveAndFlush(order); //저장하면서 em.flush 호출, 영속성 컨텍스트 객체를 DB indert문 전송, save만 하면 영속성 컨텍스트에만 저장 트랜잭션 끝날 때 flush 호출
        System.out.println("save order flush 완료");
        System.out.println("영속성 컨텍스트 클리어");
        em.clear();

        Order savedOrder = orderRepository.findById(order.getId()).orElseThrow(EntityNotFoundException::new);
        assertEquals(3, savedOrder.getOrderItems().size());
    }

}

 

 

saveAndFlush() 호출시 출력된 쿼리문

 

 

 

orders와 oder_item에 insert 쿼리가 전송된다. 중간에 테스트를 위해 영속석 컨텍스트 초기화를 해두었는데(em.clear()) 그래서 Order 객체 조회시 select 쿼리가 실행된다.

 

Comments