스프링 영속성 전이, 고아 Entity 삭제

심규원·2024년 8월 21일

CASCADE : PERSIST

@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);
}
@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);// 외래 키(연관 관계) 설정
		}
}
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);// 외래 키(연관 관계) 설정
		}
}
@OneToMany(mappedBy = "user", cascade = CascadeType.PERSIST)
    @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);
}

한번의 save 로 연관된 녀석들을 모조리 저장할 수 있다.

@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);// 외래 키(연관 관계) 설정
    }
}

영속성 삭제도 하려면

@OneToMany(mappedBy = "user", cascade = {CascadeType.PERSIST, CascadeType.REMOVE}) 처럼 해주면된다.

이런식으로 영속성 삭제 옵션을 적용하면 해당 Entity 객체를 삭제 했을 떄 연관된 Entity 객체들을 자동으로 삭제할 수 있다.
하지만 REMOVE 옵션 같은 경우 연관된 Entity 와 관계를 제거했다고 해서 자동으로 해당 Entity 가 삭제되지는 않는다.

@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, orphanRemoval = true)
    private List<Food> foodList = new ArrayList<>();

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

@OneToMany(mappedBy = "user", cascade = CascadeType.PERSIST, orphanRemoval = true)
이렇게 orphanRemoval = true 를 설정해주면된다.

⚠️ 주의!
  • orphanRemoval이나 REMOVE 옵션을 사용할 때 삭제하려고 하는 연관된 Entity를 다른 곳에서 참조하고 있는지 아닌지를 꼭 확인
    - A와 B에 참조되고 있던 C를 B를 삭제하면서 같이 삭제하게 되면 A는 참조하고 있던 C가 사라졌기 때문에 문제가 발생할 수 있다.
    - 따라서 orphanRemoval 같은 경우 @ManyToOne 같은 애너테이션에서는 사용할 수 없다.
    - ManyToOne이 설정된 Entity는 해당 Entity 객체를 참조하는 다른 Entity 객체들이 있을 수 있기 때문에 속성으로 orphanRemoval를 가지고 있지 않다.

0개의 댓글