관리 메뉴

샐님은 개발중

#6. 고아 객체 제거 와 지연 로딩 본문

포토폴리오/Spring-Boot,JPA - 쇼핑몰사이트 v2

#6. 고아 객체 제거 와 지연 로딩

샐님 2023. 8. 4. 17:20
728x90
반응형

고아 객체 제거 : 부모 엔티티와 연관 관계가 끊어진 자식 엔티티를 고아 객체라고 한다. 

영속성 전이 기능과 같이 사용시 부모 엔티티 통해 자식의 생명 주기를 함계 관리가능하다. 

고아 객체 제거 기능은 참조하는 곳이 하나일 때만 사용가능하다. 

@OneToOne, @OneToMany 어노테이션에 옵션으로 사용.

 

1. order 엔티티에 orderItems 필드에 OneToMany 옵션 추가

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

2.order 엔티티에서(부모) orderItem (자식)을 삭제 했을 때 orderItem 엔티티가 삭제 되는지 테스트 코드 작성

package won.shop.entity;

import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityNotFoundException;
import jakarta.persistence.PersistenceContext;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.transaction.annotation.Transactional;
import won.shop.Repository.ItemRepository;
import won.shop.Repository.MemberRepository;
import won.shop.Repository.OrderRepository;
import won.shop.constant.ItemSellStatus;
import won.shop.domain.*;
import won.shop.dto.MemberFormDto;

import java.time.LocalDateTime;

import static org.junit.Assert.assertEquals;

@SpringBootTest
@Transactional
public class OrderTest {

    @Autowired
    OrderRepository orderRepository;
    @Autowired
    ItemRepository itemRepository;
    @Autowired
    MemberRepository memberRepository;
    @Autowired
    PasswordEncoder passwordEncoder;

    @PersistenceContext
    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(1000);
            orderItem.setOrder(order);
            order.getOrderItems().add(orderItem);
        }

        orderRepository.saveAndFlush(order);
        em.clear();

        Order savedOrder = orderRepository.findById(order.getId())
                .orElseThrow(EntityNotFoundException::new);
        assertEquals(3, savedOrder.getOrderItems().size());
    }
    public Order createOrder(){
        Order order = new Order();
        for(int i=0;i<3;i++){
            Item item = createItem();
            itemRepository.save(item);
            OrderItem orderItem = new OrderItem();
            orderItem.setItem(item);
            orderItem.setCount(10);
            orderItem.setOrderPrice(1000);
            orderItem.setOrder(order);
            order.getOrderItems().add(orderItem);
        }

        Member member =this.createMember();
        memberRepository.save(member);
        order.setMember(member);
        orderRepository.save(order);
        return order;
    }
    public Member createMember(){
        MemberFormDto memberFormDto = new MemberFormDto();
        memberFormDto.setEmail("test@gmail.com");
        memberFormDto.setName("홍길동");
        memberFormDto.setAddress("부산시 행복동");
        memberFormDto.setPassword("1234");
        return  Member.CreateMember(memberFormDto,passwordEncoder);
    }

    @Test
    @DisplayName("고아객체 제거 테스트")
    public void orphanRemovalTest(){
        Order order = this.createOrder();
        order.getOrderItems().remove(0);
        em.flush();
    }


}
orphanRemovalTest 를 실행했을 때 부모 엔티티ㅘ 연관 관계가 끊어졌기 때문에 고아 객체를 삭제하는 쿼리 문이 실행된다.

지연로딩 : 엔티티를 조회할 때 연관된 엔티티를 함께 조회한다. 

 - 지연로딩을 배우기 전 주문 데이터 자장 후 OrderItem 엔티티를 조회해보자.

1. OrderItemRepository 인터페이스 생성

package won.shop.Repository;

import org.springframework.data.jpa.repository.JpaRepository;
import won.shop.domain.Order;
import won.shop.domain.OrderItem;

public interface OrderItemRepository extends JpaRepository<OrderItem,Long> {

}

2. 기존 테스트에가다 orderItem 조회 테스트 추가

   @Test
    @DisplayName("지연 로딩 테스트")
    public void lazyLoadingTest(){
        Order order = this.createOrder();
        Long orderItemId = order.getOrderItems().get(0).getId();
        em.flush();
        em.clear();
        OrderItem orderItem = orderItemRepository.findById(orderItemId)
                .orElseThrow(EntityNotFoundException::new);
        System.out.println("Order class : " + orderItem.getOrder().getClass());
        System.out.println("===========================");
        orderItem.getOrder().getOrderDate();
        System.out.println("===========================");
    }

OrderItem 을 조회하는데 연관된 엔티티를 전부 조회하는 것을 알 수 있다. 

SELECT o1_0.order_item_id,
       o1_0.count,
       i1_0.item_id,
       i1_0.item_detail,
       i1_0.item_nm,
       i1_0.item_sell_status,
       i1_0.price,
       i1_0.stock_number,
       o2_0.order_id,
       m1_0.member_id,
       m1_0.address,
       m1_0.contact,
       m1_0.email,
       m1_0.gender,
       m1_0.NAME,
       m1_0.password,
       m1_0.role,
       o2_0.order_date,
       o2_0.order_status,
       o2_0.reg_time,
       o2_0.update_time,
       o1_0.order_price,
       o1_0.reg_time,
       o1_0.update_time
FROM   order_item o1_0
       LEFT JOIN item i1_0
              ON i1_0.item_id = o1_0.item_id
       LEFT JOIN orders o2_0
              ON o2_0.order_id = o1_0.order_id
       LEFT JOIN member m1_0
              ON m1_0.member_id = o2_0.member_id
WHERE  o1_0.order_item_id = 1;

3. OrderItem엔티티의 item,order 필트를 지연 로딩으로 변경 후 다시 조회

package won.shop.domain;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;

import java.time.LocalDateTime;

@Getter
@Setter
@Entity
public class OrderItem {
    @Id
    @GeneratedValue
    @Column(name="order_item_id")
    private Long id;
    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="item_id")
    private Item item;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="order_id")
    private Order order;

    private int orderPrice;
    private int count;
    private LocalDateTime regTime;
    private LocalDateTime updateTime;
}

4. 테스트 다시 실행 후 sql문을 보면 orderItem만 조회 된것을 볼수 있다.

SELECT o1_0.order_item_id,
       o1_0.count,
       o1_0.item_id,
       o1_0.order_id,
       o1_0.order_price,
       o1_0.reg_time,
       o1_0.update_time
FROM   order_item o1_0
WHERE  o1_0.order_item_id = 1;

 

 

 

 

728x90
반응형