@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가 음식을 주문하기 위해서는 위 코드처럼 user, food, food2 모두 직접 save()
메서드를 호출하면서 영속화해야함
JPA에서는 간편하게 처리하기 위해 영속성 전이(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);// 외래 키(연관 관계) 설정
}
}
cascade = CascadeType.PERSIST
@OneToMany
Annotation에 영속성 전이를 적용해서 음식 Entity도 자동으로 저장될 수 있도록 만듦userRepository.save(user)
: 한 줄로 food까지 자동으로 저장됨
연관된 Entity 삭제
Robbie가 주문 APP을 탈퇴하려고 할 때 주문한 음식 정보들을 모두 삭제하고 싶을 땐?
@Test
@Transactional // 지연 로딩(Many)이기 때문에 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
Annotation에 연관된 Entity도 자동으로 삭제될 수 있도록 REMOVE 옵션 추가
userRepository.delete(user)
: 한 줄로 food까지 자동으로 삭제되게 할 수 있음 CASCADE의 REMOVE 옵션을 적용하면 해당 Entity 객체를 삭제했을 때 연관된 Entity 객체들이 자동으로 삭제할 수 있었음
@Test
@Transactional
@Rollback(value = false)
@DisplayName("연관관계 제거")
void test1() {
// 고객 Robbie 를 조회합니다.
User user = userRepository.findByName("Robbie");
System.out.println("user.getName() = " + user.getName());
// 연관된 음식 Entity 제거 : 후라이드 치킨
Food chicken = null;
for (Food food : user.getFoodList()) {
if(food.getName().equals("후라이드 치킨")) {
chicken = food;
}
}
if(chicken != null) {
user.getFoodList().remove(chicken);
}
// 연관관계 제거 확인
for (Food food : user.getFoodList()) {
System.out.println("food.getName() = " + food.getName());
}
}
후라이드 치킨 Entity 객체와 연관 관계를 제거했지만 Delete SQL이 수행되지 않음
JPA에서는 이를 간편하게 처리할 수 있는 방법으로 orphanRemoval 옵션 제공
@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);// 외래 키(연관 관계) 설정
}
}
orphanRemoval = true
default : false
연관 괸계를 제거하는 것만으로도 해당하는 Entity 삭제 가능
REMOVE 옵션과 마찬가지로 해당 Entity 즉, Robbie Entity 객체를 삭제하면 연관된 음식 Entity들이 자동으로 삭제됨
Delete SQL이 수행되어 후라이드 치킨 데이터가 삭제됨
⚠️ orphanRemoval이나 REMOVE 옵션을 사용할 때 삭제하려고 하는 연관된 Entity를 다른 곳에서 참조하고 있는지 아닌지를 꼭 확인해야함!!
- A와 B에 참조되고 있던 C를 B를 삭제하면서 같이 삭제하게 되면 A는 참조하고 있던 C가 사라졌기 때문에 문제가 발생할 수 있음
- 따라서 orphanRemoval 같은 경우 @ManyToOne 같은 Annotation에서는 사용 X
- ManyToOne이 설정된 Entity는 해당 Entity 객체를 참조하는 다른 Entity 객체들이 있을 수 있기 때문에 속성으로 orphanRemoval를 가지고 있지 X