Engineering Note

[JPA] 영속성 전이 옵션과 연관관계 편의 메서드가 필요한 이유, 고아객체 설정 본문

Server/JPA ORM

[JPA] 영속성 전이 옵션과 연관관계 편의 메서드가 필요한 이유, 고아객체 설정

Software Engineer Kim 2026. 1. 10. 21:22

 

영속성 전이 옵션과 연관관계 편의 메서드가 필요한 이유

 

cascade = CascadeType.ALL

 

cacade 영속성 전이 옵션은 부모의 영속상태의 생명주기를 자식과 함께 하겠다는 설정이다.

 

 

 

JPA를 사용하면 연관관계 관리를 편리하기 위해 연관관계 편의 메서드라고 불리는 메서드를 만들어서 사용한다.

아래 메서드는 연관관계를 편리하게 하기 위한 메서드이다. '연관관계 편의 메서드'라고 특별한 이름이 붙었지만, Java입장에서는 필드를 세팅해주는 메서드다. 하지만 JPA는 Java의 객체 세계와 DB의 관계형 세계를 함께 다루어야 하기 때문에 Java의 철학과는 조금은 다른 형태로 존재하는 메서드라 처음에는 낯설 수 있다.

 

public void addOrderItem(OrderItem orderItem) {
    orderItems.add(orderItem);
    orderItem.setOrder(this);
}

 

 

이커머스 환경에서 Order엔티티와 OrderItem엔티티가 있다. OrderItem 엔티티는 Order가 없이는 존재할 수 없는 엔티티다. DB관점에서 OrderItem은 Order의 기본키를 참조하는 외래키를 갖는다.

 

비즈니스 로직에서 주문이 생성되어서 Order Entity가 생성되고 저장해야 하는 상황에서 OrderItem도 따로 저장하지 않고 한 번에 관리하기 위해 연관관계 메서드가 필요하다.


주문 생성 정적 팩토리 메서드

public static Order createOrder(Member member, List<OrderItem> orderItemList) {
    Order order = new Order();

    //연관관계 세팅
    order.setMember(member);
    for(OrderItem orderItem : orderItemList) {
        order.addOrderItem(orderItem);
    }

    order.setOrderStatus(OrderStatus.ORDER);
    order.setOrderDate(LocalDateTime.now());
    return order;
}

 

 

주문 서비스 로직

public Long order(OrderDto orderDto, String email) {
    Item item = itemRepository.findByIdForUpdate(orderDto.getItemId()).orElseThrow(EntityNotFoundException::new);
    Member member = memberRepository.findByEmail(email);

    List<OrderItem> orderItemList = new ArrayList<>();

    OrderItem orderItem = OrderItem.createOrderItem(item, orderDto.getQuantity());
    orderItemList.add(orderItem);

    Order order = Order.createOrder(member, orderItemList);
    log.info("========주문 정보 저장=========");
    orderRepository.save(order);
    log.info("========주문 정보 저장 완료=========");

    return order.getId();
}

 

 

주문 서비스 로직을 보면 OrderItem은 저장하는 코드가 없다. 오직 주문을 저장하는 ' orderRepository.save(order);' 코드만 있다.

========주문 정보 저장=========
2026-01-10T21:12:01.257+09:00 DEBUG 82173 --- [shop] [           main] org.hibernate.SQL                        : 
    insert 
    into
        orders
        (created_by, member_id, modified_by, order_date, order_status, reg_time, order_id) 
    values
        (?, ?, ?, ?, ?, ?, default)

    insert 
    into
        order_item
        (created_by, item_id, modified_by, order_id, order_price, quantity, reg_time, order_item_id) 
    values
        (?, ?, ?, ?, ?, ?, ?, default)
========주문 정보 저장 완료=========

 

 

코드를 실행시켜 보면 order를 저장하는 SQL과 order_item을 저장하는 SQL이 실행되었다. 이러한 일이 가능한 이유는 '영속성 전이 설정'때문이다. 그리고 영속성 전이 설정이 정상 동작하도록 하기위해  orderRepository(order);를 수행하기 전에 연관관계 편의 메서드를 통해 orderItem을 세팅해주었기 때문이다.

 

그런데 여기서 주의할 점은 JPA에서 외래키는 부모가 아니라 자식 엔티티가 관리하기 때문에 부모에 있는 List<OrderItem> orderItems만 세팅해준다고 외래키가 세팅되지는 않는다. 위에 addOrderItem(OrderItem orderItem) 메서드 내부에서 두 줄의 코드가 있는 이유다. 첫 줄에 orderItems.add(orderItem);은 Order엔티티의 'orderItems' 필드를 채우기 위한 코드고, 두 번째 줄의 orderItems.setOrder(this);가 외래키를 세팅해주기 위한 코드다. 다시 한 번 강조하지만 JPA는 연관관계주인인 자식 엔티티가 외래키를 관리하기 때문이다.

그런데 외래키를 관리한다고 두 번째 줄만 작성하는 것도 오류를 발생시킬 수 있다. Java 객체로 생성된 Order entity의 orderItems이 세팅되지 않으면 맥락적으로 문제가 있기 때문이다. JPA는 트랜잭션 안에서 성능을 위해 Entity를 1차캐시에 저장하고 활용하는데 Order가 다시 조회되어 orderItems를 조회한다면, 외래키만 생각한 코드는 오류를 발생시킨다.

 

 

Comments