스프링 - JPA 영속성 전이

2024. 8. 28. 09:30스프링

24/08/28

 

 

지난 포스팅에서 영속성 컨텍스트에 알아보았습니다.
오늘은 더 나아가 영속성 전이가
무엇인지 코드를 보면서 이해해봅시다.

 


 

 

※   영속성 전이에 대해 알아보자.

📌 영속성 전이: CASCADE는 언제 사용하는가?

     특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속 상태로 만들고 싶으면 영속성 전이(Transitive persistence) 기능을 사용하면 된다. JPA는 CASCADE 옵션으로 영속성 전이를 제공하는데 영속성 전이를 사용하면 부모 엔티티를 저장할 때 자식 엔티티도 함께 저장할 수 있다.

 

   ▶  CASCADE : PERSIST



           ●  음식 테이블과 고객 테이블이 N : 1 양방향 관계라 가정해보자.

     ➡️ CascadeTest

package com.sparta.jpaadvance.cascade;

import com.sparta.jpaadvance.repository.FoodRepository;
import com.sparta.jpaadvance.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class CascadeTest {

    @Autowired
    UserRepository userRepository;
    @Autowired
    FoodRepository foodRepository;


}


           ●  고객 ‘Robbie’가 후라이드 치킨과 양념 치킨을 주문해보자.

@Test
@DisplayName("Robbie 음식 주문")
void test1() {
    // 고객 Robbie 가 후라이드 치킨과 양념 치킨을 주문합니다.
    User user = new User();
    user.setName("Robbie");

    // 후라이드 치킨 주문
    Food food = new Food();
    food.setName("후라이드 치킨");
    food.setPrice(15000);

    user.addFoodList(food);

    Food food2 = new Food();
    food2.setName("양념 치킨");
    food2.setPrice(20000);

    user.addFoodList(food2);

    userRepository.save(user);
    foodRepository.save(food);
    foodRepository.save(food2);
}


     ➡️ Robbie 음식 주문

@Entity
@Getter
@Setter
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @OneToMany(mappedBy = "user")
    private List<Food> foodList = new ArrayList<>();

		public void addFoodList(Food food) {
			  this.foodList.add(food);
			  food.setUser(this);// 외래 키(연관 관계) 설정
		}
}


           ●  외래 키(연관 관계) 설정을 위해 addFoodList 메서드를 고객 Entity에 추가하자.

userRepository.save(user);
foodRepository.save(food);
foodRepository.save(food2);


           ●  Robbie가 음식을 주문하기 위해서는 위 처럼 user, food, food2 모두 직접
                save() 메서드
 호출하면서 영속화해야한다.

            ✅  JPA에서는 이를 간편하게 처리할 수 있는 방법으로 영속성 전이(CASCADE)의
                   PERSIST 옵션을 제공한다.
              ○  영속성 전이란
                  ▪  영속 상태의 Entity에서 수행되는 작업들이 연관된 Entity까지 전파되는 상황
                     뜻한다.
              ○  속성 전이를 적용하여 해당 Entity를 저장할 때 연관된 Entity까지 자동으로
                 저장하기 위해서는 자동으로 저장하려고 하는 연관된 Entity에 추가한
                 연관관계 애너테이션에 CASCADE의 PERSIST 옵션을 설정하면 된다.

 

@Entity
@Getter
@Setter
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @OneToMany(mappedBy = "user", cascade = CascadeType.PERSIST)
    private List<Food> foodList = new ArrayList<>();

		public void addFoodList(Food food) {
			  this.foodList.add(food);
			  food.setUser(this);// 외래 키(연관 관계) 설정
		}
}

           ●  고객 Entity의  @OneToMany 애너테이션에 영속성 전이를 적용해서 음식 Entity도
                자동으로 저장될 수 있도록 만든다.

     ➡️ 영속성 전이 저장

@Test
@DisplayName("영속성 전이 저장")
void test2() {
    // 고객 Robbie 가 후라이드 치킨과 양념 치킨을 주문합니다.
    User user = new User();
    user.setName("Robbie");

    // 후라이드 치킨 주문
    Food food = new Food();
    food.setName("후라이드 치킨");
    food.setPrice(15000);

    user.addFoodList(food);

    Food food2 = new Food();
    food2.setName("양념 치킨");
    food2.setPrice(20000);

    user.addFoodList(food2);

    userRepository.save(user);
}

           ●  CASCADE 설정을 적용했기 때문에 직접 음식 Entity 객체 food, food2를
              영속 상태로 만들지 않아도 자동으로 잘 저장이 된다.

 

 

   ▶  CASCADE : REMOVE

           ●  이번에는 연관된 Entity를 손쉽게 삭제하는 방법에 대해서 학습해보자.
           ●  Robbie가 주문 APP을 탈퇴한다고 하자.
              ○  주문한 음식 정보들을 모두 삭제하려고 하는데 어떻게 하면 될까❓

@Entity
@Getter
@Setter
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @OneToMany(mappedBy = "user")
    private List<Food> foodList = new ArrayList<>();

    public void addFoodList(Food food) {
        this.foodList.add(food);
        food.setUser(this);// 외래 키(연관 관계) 설정
    }
}

           ●   cascade = CascadeType.PERSIST  제거

@Test
@Transactional
@Rollback(value = false)
@DisplayName("Robbie 탈퇴")
void test3() {
    // 고객 Robbie 를 조회합니다.
    User user = userRepository.findByName("Robbie");
    System.out.println("user.getName() = " + user.getName());

    // Robbie 가 주문한 음식 조회
    for (Food food : user.getFoodList()) {
        System.out.println("food.getName() = " + food.getName());
    }

    // 주문한 음식 데이터 삭제
    foodRepository.deleteAll(user.getFoodList());

    // Robbie 탈퇴
    userRepository.delete(user);
}

           ●  주문한 음식 데이터를 삭제하기 위해서  지연 로딩 된 음식 Entity들을 가져와 직접
               삭제해준다.
           ●  그 후 Robbie 고객의 Entity를 삭제한다.

           ✅ JPA에서는 이를 간편하게 처리할 수 있는 방법으로 영속성 전이(CASCADE)
                 REMOVE 옵션을 제공한다.

@Entity
@Getter
@Setter
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @OneToMany(mappedBy = "user", cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
    private List<Food> foodList = new ArrayList<>();

    public void addFoodList(Food food) {
        this.foodList.add(food);
        food.setUser(this);// 외래 키(연관 관계) 설정
    }
}

           ●  cascade = {CascadeType.PERSIST, CascadeType.REMOVE} 이렇게 중복으로
              옵션을 설정할 수도 있다.

           ●  고객 Entity의 @OneToMany 애너테이션에 연관된 음식 Entity도 자동으로 삭제될 수
               있도록 REMOVE 옵션을 추가한다.

     ➡️ 영속성 전이 삭

@Test
@Transactional
@Rollback(value = false)
@DisplayName("영속성 전이 삭제")
void test4() {
    // 고객 Robbie 를 조회합니다.
    User user = userRepository.findByName("Robbie");
    System.out.println("user.getName() = " + user.getName());

    // Robbie 가 주문한 음식 조회
    for (Food food : user.getFoodList()) {
        System.out.println("food.getName() = " + food.getName());
    }

    // Robbie 탈퇴
    userRepository.delete(user);
}

           ●  Robbie 고객 Entity 객체를 조회한 후 해당 객체를 delete 하자 자동으로 연관된
               음식 데이터들이 삭제되었다.

 

 

 

 

 


※ 위 이미지들은 스파르타코딩클럽에 저작권이 있으므로 무단 도용 금지 및 상업 목적으로 사용할 수 없습니다.

 

'스프링' 카테고리의 다른 글

스프링 - 통합 테스트란?  (0) 2024.08.26
스프링 - Mockito란?  (0) 2024.08.23
스프링 - 테스트 사용 방법  (0) 2024.08.22
스프링 - JPA Query Methods란 무엇일까?  (0) 2024.08.21
스프링 - JPA Auditing 적용하기  (0) 2024.08.20