๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๋ฐฑ์—”๋“œ/spring&springboot

[๋‚ด์ผ๋ฐฐ์›€์บ ํ”„_Spring Boot ์ˆ™๋ จ์ฃผ์ฐจ] JPA ํ•œ ๊ฑธ์Œ ๋” ๋‚˜์•„๊ฐ€๊ธฐ(์ง€์—ฐ ๋กœ๋”ฉ๊ณผ ์ฆ‰์‹œ ๋กœ๋”ฉ, ์˜์†์„ฑ ์ „์ด, ๊ณ ์•„ Entity)

by alswlfl 2026. 6. 30.

์ง€์—ฐ ๋กœ๋”ฉ(LAZY)๊ณผ ์ฆ‰์‹œ ๋กœ๋”ฉ(EAGER)

  • ์Œ์‹ ํ…Œ์ด๋ธ”๊ณผ ๊ณ ๊ฐ ํ…Œ์ด๋ธ”์ด N:1 ๊ด€๊ณ„๋กœ ์„ค์ •ํ•˜๊ณ  ์‹œ์ž‘
@Entity
@Getter
@Setter
@Table(name = "food")
public class Food {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private double price;

    @ManyToOne()
    @JoinColumn(name = "user_id")
    private User user;
}
@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<>();
}
  • ์ง€์—ฐ๋กœ๋”ฉ๊ณผ ์ฆ‰์‹œ๋กœ๋”ฉ ํ•™์Šต์— ํ•„์š”ํ•œ ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ ๋ฏธ๋ฆฌ ์ถ”๊ฐ€ํ•ด๋†“๊ธฐ
@Test
@Transactional
@Rollback(value = false)
void init() {
    // ์ง€์—ฐ ๋กœ๋”ฉ ์‹œ ํ•„์š”ํ•œ ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ ๋ฏธ๋ฆฌ ๋งŒ๋“ค๊ธฐ
    List<User> userList = new ArrayList<>();
    User user1 = new User();
    user1.setName("Robbie");
    userList.add(user1);

    User user2 = new User();
    user2.setName("Robbert");
    userList.add(user2);
    userRepository.saveAll(userList); // saveAll(): ํ•œ ๋ฒˆ์— ์—ฌ๋Ÿฌ๊ฐœ save ๊ฐ€๋Šฅ

    List<Food> foodList = new ArrayList<>();
    Food food1 = new Food();
    food1.setName("๊ณ ๊ตฌ๋งˆ ํ”ผ์ž");
    food1.setPrice(30000);
    food1.setUser(user1); // ์™ธ๋ž˜ ํ‚ค(์—ฐ๊ด€ ๊ด€๊ณ„) ์„ค์ •
    foodList.add(food1);

    Food food2 = new Food();
    food2.setName("์•„๋ณด์นด๋„ ํ”ผ์ž");
    food2.setPrice(50000);
    food2.setUser(user1); // ์™ธ๋ž˜ ํ‚ค(์—ฐ๊ด€ ๊ด€๊ณ„) ์„ค์ •
    foodList.add(food2);

    Food food3 = new Food();
    food3.setName("ํ›„๋ผ์ด๋“œ ์น˜ํ‚จ");
    food3.setPrice(15000);
    food3.setUser(user1); // ์™ธ๋ž˜ ํ‚ค(์—ฐ๊ด€ ๊ด€๊ณ„) ์„ค์ •
    foodList.add(food3);

    Food food4 = new Food();
    food4.setName("ํ›„๋ผ์ด๋“œ ์น˜ํ‚จ");
    food4.setPrice(15000);
    food4.setUser(user2); // ์™ธ๋ž˜ ํ‚ค(์—ฐ๊ด€ ๊ด€๊ณ„) ์„ค์ •
    foodList.add(food4);

    Food food5 = new Food();
    food5.setName("๊ณ ๊ตฌ๋งˆ ํ”ผ์ž");
    food5.setPrice(30000);
    food5.setUser(user2); // ์™ธ๋ž˜ ํ‚ค(์—ฐ๊ด€ ๊ด€๊ณ„) ์„ค์ •
    foodList.add(food5);
    foodRepository.saveAll(foodList);
}

์ถ”๊ฐ€๋œ ๋ฐ์ดํ„ฐ

์•„๋ณด์นด๋„ ํ”ผ์ž๋ฅผ ์กฐํšŒํ•˜๋Š” ํ…Œ์ŠคํŠธ

@Test
@DisplayName("์•„๋ณด์นด๋„ ํ”ผ์ž ์กฐํšŒ")
void test1() {
    Food food = foodRepository.findById(2L).orElseThrow(NullPointerException::new);

    System.out.println("food.getName() = " + food.getName());
    System.out.println("food.getPrice() = " + food.getPrice());
}

ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ

FetchType

JPA์—์„œ๋Š” ์—ฐ๊ด€ ๊ด€๊ณ„๊ฐ€ ์„ค์ •๋œ Entity์˜ ์ •๋ณด๋ฅผ ๋ฐ”๋กœ ๊ฐ€์ ธ์˜ฌ์ง€ ํ˜น์€ ํ•„์š”ํ•  ๋•Œ ๊ฐ€์ ธ์˜ฌ์ง€ ์ •ํ•  ์ˆ˜ ์žˆ์Œ! ์ฆ‰, ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ๋ฒ•์ด FetchType์ž„

FetchType ์ข…๋ฅ˜ 1) LAZY(์ง€์—ฐ ๋กœ๋”ฉ)

LAZY๋Š” ํ•„์š”ํ•œ ์‹œ์ ์— ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ด

  • @OneToMany๋Š” default๊ฐ€ LAZY์ž„
  • ์„ค์ •๋œ ํ•ด๋‹น ํ•„๋“œ๊ฐ€ Java Collection ํƒ€์ž…์ธ ๊ฒฝ์šฐ, ์ฆ‰ @?ToMany ํ˜•ํƒœ์ธ ๊ฒฝ์šฐ ํšจ์œจ์ ์œผ๋กœ ์ •๋ณด๋ฅผ ์กฐํšŒํ•˜๊ธฐ ์œ„ํ•ด default๊ฐ€ LAZY์ž„

FetchType ์ข…๋ฅ˜ 2) EAGER(์ฆ‰์‹œ ๋กœ๋”ฉ)

EAGER์€ ์ฆ‰์‹œ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ด

  • @ManyToOne์€ default๊ฐ€ EAGER์ž„
  • @?ToOne ํ˜•ํƒœ์ธ ๊ฒฝ์šฐ ์ฆ‰์‹œ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์™€๋„ ๋ฌด๋ฆฌ๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— default๊ฐ€ EAGER์ž„

Robbie ๊ณ ๊ฐ ์กฐํšŒ(LAZY)

@Test
@Transactional
@DisplayName("Robbie ๊ณ ๊ฐ ์กฐํšŒ")
void test2() {
    User user = userRepository.findByName("Robbie");
    System.out.println("user.getName() = " + user.getName());

    System.out.println("Robbie๊ฐ€ ์ฃผ๋ฌธํ•œ ์Œ์‹ ์ด๋ฆ„ ์กฐํšŒ");
    for (Food food : user.getFoodList()) {
        System.out.println(food.getName());
    }
}

๊ฒฐ๊ณผ

์•„๋ณด์นด๋„ ํ”ผ์ž ์กฐํšŒ ์‹œ ํ”ผ์ž๋งŒ ์กฐํšŒํ•˜๊ณ  ์‹ถ์„ ๊ฒฝ์šฐ

  • FetchType์„ LAZY๋กœ ๋ณ€๊ฒฝ
@Entity
...
@Table(name = "food")
public class Food {
    ...

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;
}

ํ…Œ์ŠคํŠธ ์‹คํŒจ

 

  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ ๊ธฐ๋Šฅ: 1์ฐจ ์บ์‹œ, ์“ฐ๊ธฐ ์ง€์—ฐ ์ €์žฅ์†Œ, ๋ณ€๊ฒฝ ๊ฐ์ง€
  • ์ง€์—ฐ ๋กœ๋”ฉ๋„ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜์ž„
  • ์ฆ‰, ์ง€์—ฐ ๋กœ๋”ฉ๋œ Entity ์ •๋ณด๋ฅผ ์กฐํšŒ(Select)ํ•˜๋ ค๊ณ  ํ•  ๋•Œ๋Š” ๋ฐ˜๋“œ์‹œ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ ์กด์žฌํ•ด์•ผ ํ•จ
  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ ์กด์žฌํ•ด์•ผ ํ•œ๋‹ค = Spring Container ํ™˜๊ฒฝ์—์„œ๋Š” ํŠธ๋žœ์žญ์…˜์ด ์ ์šฉ๋˜์–ด ์žˆ์–ด์•ผ ํ•จ
@Test
@Transactional
@DisplayName("์•„๋ณด์นด๋„ ํ”ผ์ž ์กฐํšŒ")
void test1() {
    Food food = foodRepository.findById(2L).orElseThrow(NullPointerException::new);

    System.out.println("food.getName() = " + food.getName());
    System.out.println("food.getPrice() = " + food.getPrice());

    System.out.println("์•„๋ณด์นด๋„ ํ”ผ์ž๋ฅผ ์ฃผ๋ฌธํ•œ ํšŒ์› ์ •๋ณด ์กฐํšŒ");
    System.out.println("food.getUser().getName() = " + food.getUser().getName());
}

๊ฒฐ๊ณผ: ํšŒ์› ์ •๋ณด๋Š” ๋‚˜์ค‘์— ๊ฐ€์ ธ์˜ด

 


์˜์†์„ฑ ์ „์ด(Cascade Persist)

  • ์Œ์‹ ํ…Œ์ด๋ธ”๊ณผ ๊ณ ๊ฐ ํ…Œ์ด๋ธ”์ด N:1 ๊ด€๊ณ„๋กœ ์„ค์ •ํ•˜๊ณ  ์‹œ์ž‘

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);
}
@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); // ์™ธ๋ž˜ ํ‚ค ์„ค์ •
    }
}
  • ํ›„๋ผ์ด๋“œ์™€ ์น˜ํ‚จ ๊ฐ๊ฐ saveํ•˜๊ณ  User saveํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค, User Entity๋งŒ saveํ•˜๋ฉด foodList์— ๋“ค์–ด์žˆ๋Š” Food 2๊ฐœ๊ฐ€ ์ž๋™์œผ๋กœ ์˜์†ํ™”๋˜๋Š” ๊ฒƒ์ด ๋” ํšจ์œจ์ ์ž„
  • JPA์—์„œ๋Š” ์ด๋Ÿฌํ•œ ๋‚ด์šฉ์„ ์•„์ฃผ ๊ฐ„ํŽธํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ์˜์†์„ฑ ์ „์ด์— persist ์˜ต์…˜ ์ œ๊ณต

Cascade์˜ persist(์˜์†์„ฑ ์ „์ด)

์˜์†์„ฑ ์ „์ด๋Š” ์˜์† ์ƒํƒœ์˜ Entity์—์„œ ์ˆ˜ํ–‰๋˜๋Š” ์ž‘์—…๋“ค์ด ์—ฐ๊ด€๋œ Entity๊นŒ์ง€ ์ „ํŒŒ๋˜๋Š” ์ƒํ™ฉ
  • ์˜์†์„ฑ ์ „์ด๋ฅผ ์‚ฌ์šฉํ•ด์„œ Entity๋ฅผ ์ €์žฅํ•  ๋•Œ, ํ•ด๋‹น Entity์™€ ์—ฐ๊ด€ ๊ด€๊ณ„์— ์žˆ๋Š” Entity๊นŒ์ง€ ์ž๋™์œผ๋กœ ์ €์žฅํ•  ์ˆ˜ ์žˆ์Œ
  • ์—ฐ๊ด€๋œ Entity์— Cascade persist option ์„ค์ •ํ•˜๋ฉด๋จ
@Entity
...
@Table(name = "users")
public class User {
    ...

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

    ...
}

 

์˜์†์„ฑ ์ „์ด ์ €์žฅ ํ…Œ์ŠคํŠธ

@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๋ฅผ 1๋ฒˆ๋งŒ ์ž‘์„ฑํ•ด๋„ 3๊ฐœ์˜ INSERT ์ฟผ๋ฆฌ๊ฐ€ ๋‚ ์•„๊ฐ

 

์˜์†์„ฑ ์ „์ด ์‚ญ์ œ ํ…Œ์ŠคํŠธ

  • ์˜์†์„ฑ ์ „์ด๋ฅผ ์ ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด, ์ฃผ๋ฌธํ•œ ์Œ์‹ ๋ฐ์ดํ„ฐ๋ฅผ ๋จผ์ € ์‚ญ์ œํ•œ ํ›„ ํšŒ์› ํƒˆํ‡ด๋ฅผ ํ•ด์•ผํ•จ
@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);
}
  • JPA์—์„œ Cascade.REMOVE ์˜ต์…˜ ์ œ๊ณตํ•จ
@Entity
...
@Table(name = "users")
public class User {
    ...

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

    ...
}
@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);
}

๋‚ด๋ถ€์ ์œผ๋กœ ํ•œ ๋ฒˆ์— ์‚ญ์ œ ์ˆ˜ํ–‰


๊ณ ์•„ Entity(onphanRemoval)

  • ๋จผ์ € ํ…Œ์ด๋ธ” ์ƒ์„ฑ ํ›„ ๋ฐ์ดํ„ฐ ์‚ฝ์ž… ์ˆ˜ํ–‰
...

@SpringBootTest
public class OrphanTest {

    @Autowired
    UserRepository userRepository;
    @Autowired
    FoodRepository foodRepository;

    @Test
    @Transactional
    @Rollback(value = false)
    void init() {
        List<User> userList = new ArrayList<>();
        User user1 = new User();
        user1.setName("Robbie");
        userList.add(user1);

        User user2 = new User();
        user2.setName("Robbert");
        userList.add(user2);
        userRepository.saveAll(userList);

        List<Food> foodList = new ArrayList<>();
        Food food1 = new Food();
        food1.setName("๊ณ ๊ตฌ๋งˆ ํ”ผ์ž");
        food1.setPrice(30000);
        food1.setUser(user1); // ์™ธ๋ž˜ ํ‚ค(์—ฐ๊ด€ ๊ด€๊ณ„) ์„ค์ •
        foodList.add(food1);

        Food food2 = new Food();
        food2.setName("์•„๋ณด์นด๋„ ํ”ผ์ž");
        food2.setPrice(50000);
        food2.setUser(user1); // ์™ธ๋ž˜ ํ‚ค(์—ฐ๊ด€ ๊ด€๊ณ„) ์„ค์ •
        foodList.add(food2);

        Food food3 = new Food();
        food3.setName("ํ›„๋ผ์ด๋“œ ์น˜ํ‚จ");
        food3.setPrice(15000);
        food3.setUser(user1); // ์™ธ๋ž˜ ํ‚ค(์—ฐ๊ด€ ๊ด€๊ณ„) ์„ค์ •
        foodList.add(food3);

        Food food4 = new Food();
        food4.setName("์–‘๋… ์น˜ํ‚จ");
        food4.setPrice(20000);
        food4.setUser(user2); // ์™ธ๋ž˜ ํ‚ค(์—ฐ๊ด€ ๊ด€๊ณ„) ์„ค์ •
        foodList.add(food4);

        Food food5 = new Food();
        food5.setName("๊ณ ๊ตฌ๋งˆ ํ”ผ์ž");
        food5.setPrice(30000);
        food5.setUser(user2); // ์™ธ๋ž˜ ํ‚ค(์—ฐ๊ด€ ๊ด€๊ณ„) ์„ค์ •
        foodList.add(food5);
        foodRepository.saveAll(foodList);
    }
}

๋ฐ์ดํ„ฐ ์‚ฝ์ž… ๊ฒฐ๊ณผ

์—ฐ๊ด€ ๊ด€๊ณ„ ์ œ๊ฑฐ

@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;
        }
    }
    // ํ›„๋ผ์ด๋“œ ์น˜ํ‚จ์ด ์žˆ๋‹ค๋ฉด, ํ•ด๋‹นํ•˜๋Š” chicken ์ œ๊ฑฐ
    if(chicken != null) {
        user.getFoodList().remove(chicken);
    }

    // ์—ฐ๊ด€๊ด€๊ณ„ ์ œ๊ฑฐ ํ™•์ธ
    for (Food food : user.getFoodList()) {
        System.out.println("food.getName() = " + food.getName());
    }
}

๊ฒฐ๊ณผ

  • ์—ฐ๊ด€ ๊ด€๊ณ„๋Š” ์ œ๊ฑฐ๋˜์–ด ๋‚˜์˜ค์ง€๋งŒ, ์‹ค์ œ DB์—์„œ๋Š” ์ œ๊ฑฐ๋˜์ง€ ์•Š์Œ!
  • JPA์—์„œ๋Š” ์—ฐ๊ด€ ๊ด€๊ณ„๋ฅผ ์ œ๊ฑฐํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋„ ๊ทธ์— ํ•ด๋‹นํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ง€์šธ ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ orphanRemoval ์˜ต์…˜ ์ œ๊ณต
    • default๋Š” false
...
@Entity
@Getter
@Setter
@Table(name = "users")
public class User {
	...

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

    ...
}

orphanRemoval ์ ์šฉ ํ›„ ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ

  • ์—ฐ๊ด€ ๊ด€๊ณ„๋ฅผ ์ œ๊ฑฐํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋„ ํ•ด๋‹น Entity ์‚ญ์ œ๋จ!
  • orphanRemoval ์˜ต์…˜๋„ Cascade.DELETE ์˜ต์…˜๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ํ•ด๋‹น Entity๋ฅผ ์‚ญ์ œํ•˜๋ฉด ์—ฐ๊ด€๋œ Entity๋“ค์ด ์ž๋™์œผ๋กœ ์‚ญ์ œ๋จ

โš ๏ธ ์ฃผ์˜ํ•  ์ 

orphanRemoval ์˜ต์…˜๊ณผ Cascade.DELETE ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•  ๋•Œ, ์‚ญ์ œํ•˜๋ ค๊ณ  ํ•˜๋Š” ๊ทธ ์—ฐ๊ด€๋œ Entity๋ฅผ ๋‹ค๋ฅธ ๊ณณ์—์„œ๋„ ์ฐธ์กฐํ•˜๊ณ  ์žˆ๋Š”์ง€ ์•„๋‹Œ์ง€๋ฅผ ๊ผญ ํ™•์ธํ•ด์•ผ ํ•จ!

  • ๋”ฐ๋ผ์„œ, orphanRemoval ์˜ต์…˜์€ @ManyToOne ์–ด๋…ธํ…Œ์ด์…˜์€ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Œ