포토폴리오/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
반응형