카테고리 없음

영속성 전이

JABHACK 2025. 1. 16. 12:50

Cascade

📌 영속성 전이(Cascade)란 JPA에서 특정 엔티티를 저장, 삭제 등의 작업을 할 때 연관된 엔티티에도 동일한 작업을 자동으로 적용하도록 설정하는 기능이다.

  • 영속성 전이는 지연 로딩, 즉시 로딩과는 아무 관련이 없다.

코드 예시

  • 1:N, N:1 양방향 연관관계
@Entity
@Table(name = "category")
public class Category {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(mappedBy = "category")
    private List<Product> productList = new ArrayList<>();

    public Category() {
    }

    public Category(String name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public void addProduct(Product product) {
        productList.add(product);
        product.setCategory(this);
    }

}
@Entity
@Table(name = "product")
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @ManyToOne
    @JoinColumn(name = "category_id")
    private Category category;

    public Product() {
    }

    public Product(String name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public void setCategory(Category category) {
        this.category = category;
    }
}
public class Main {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("entity");

        EntityManager em = emf.createEntityManager();

        EntityTransaction transaction = em.getTransaction();

        transaction.begin();

        try {

            Category category = new Category("food");
            em.persist(category);

            Product product1 = new Product("pizza");
            Product product2 = new Product("kimchi");
            category.addProduct(product1);
            category.addProduct(product2);

            em.persist(product1);
            em.persist(product2);


            transaction.commit();
        } catch (Exception e) {
            transaction.rollback();
            e.printStackTrace();
        } finally {
            em.close();
        }

        emf.close();
    }
}

 

실행결과

 

  • 총 세번의 INSERT SQL이 실행된다.

 

  • cascade 속성 적용
    • CascadeType.ALL
@Entity
@Table(name = "category")
public class Category {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(mappedBy = "category", cascade = CascadeType.ALL)
    private List<Product> productList = new ArrayList<>();

    public Category() {
    }

    public Category(String name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public void addProduct(Product product) {
        productList.add(product);
        product.setCategory(this);
    }

}
public class CascadeMain {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("entity");

        EntityManager em = emf.createEntityManager();

        EntityTransaction transaction = em.getTransaction();

        transaction.begin();

        try {

            Category category = new Category("food");

            Product product1 = new Product("pizza");
            Product product2 = new Product("kimchi");
            category.addProduct(product1);
            category.addProduct(product2);

            em.persist(category);

            transaction.commit();
        } catch (Exception e) {
            transaction.rollback();
            e.printStackTrace();
        } finally {
            em.close();
        }

        emf.close();
    }
}

실행결과

em.persist(category) 만 해도 세번의 INSER SQL이 실행된다.

 

 

사용 방법과 주의점

📌 영속성 전이는 연관관계 매핑과는 아무 관련이 없다.

 

  • 영속성 전이(Cascade)
    • 단순히 Entity를 저장, 삭제할 때 연관된 Entity에도 동일한 작업을 적용한다.
    • 속성 종류
      1. ALL : 모두 적용
      2. PERSIST : 영속
      3. REMOVE : 삭제
      4. MERGE
      5. REFRESH
      6. DETACH
  • 사용 방법
    • 단일 Entity에 완전히 종속적인 경우 생명주기가 같다면 사용한다.
      • 블로그 글의 댓글처럼 항상 글을 통해서만 관리하는 경우
      • 상품과 상품 이미지처럼 특정 상품에 종속되어 관리되는 경우
    • 작가와 책
      • 책은 특정 작가와 연관되지만 작가가 활동하지 않아도 책은 보존된다.
      • 이런 경우는 사용하지 않는다.

 

고아 객체

📌 JPA에서 부모 엔티티와의 연관관계가 끊어진 자식 엔티티를 말한다.

  • 고아 객체 삭제
    • 부모 엔티티와 연관관계가 끊어진 자식 Entity를 자동으로 삭제한다.
    • orphanRemoval = true 사용
      • 기본 값 : false
@Entity
@Table(name = "category")
public class Category {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(mappedBy = "category", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Product> productList = new ArrayList<>();

    public Category() {
    }

    public Category(String name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }
    
    public List<Product> getProductList() {
        return productList;
    }

    public void addProduct(Product product) {
        productList.add(product);
        product.setCategory(this);
    }

}
public class OrphanRemovalMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("entity");

        EntityManager em = emf.createEntityManager();

        EntityTransaction transaction = em.getTransaction();

        transaction.begin();

        try {

            Category category = new Category("food");

            Product product1 = new Product("pizza");
            Product product2 = new Product("kimchi");
            category.addProduct(product1);
            category.addProduct(product2);

            em.persist(category);

            em.flush();
            em.clear();

            Category findCategory = em.find(Category.class, category.getId());
            findCategory.getProductList().remove(0);

            transaction.commit();
        } catch (Exception e) {
            transaction.rollback();
            e.printStackTrace();
        } finally {
            em.close();
        }

        emf.close();
    }
  • 실행결과

  • Collection에서 제거된 객체는 삭제된다.

 

  • 주의점
    1. 참조하는 곳이 하나인 경우에만 사용한다.
      • 단일 Entity에 완전히 종속적인 경우 생명주기가 같다면 사용한다.
    2. @OneToOne, @OneToMany만 사용이 가능하다.
    • 부모 Entity를 제거하면 자식 Entity는 고아 객체가 된다.
    • CascadeType.REMOVE와 비슷하게 동작한다.

 

CascadeType.ALL 제거

@Entity
@Table(name = "category")
public class Category {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

		@OneToMany(mappedBy = "category", orphanRemoval = true)
    private List<Product> productList = new ArrayList<>();

    public Category() {
    }

    public Category(String name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public List<Product> getProductList() {
        return productList;
    }

    public void addProduct(Product product) {
        productList.add(product);
        product.setCategory(this);
    }

}
public class OrphanRemovalMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("entity");

        EntityManager em = emf.createEntityManager();

        EntityTransaction transaction = em.getTransaction();

        transaction.begin();

        try {

            Category category = new Category("food");

            Product product1 = new Product("pizza");
            Product product2 = new Product("kimchi");
            category.addProduct(product1);
            category.addProduct(product2);

            em.persist(category);
            // cascade 제거
            em.persist(product1);
            em.persist(product2);

            em.flush();
            em.clear();

            // cascade 제거
            Category findCategory = em.find(Category.class, category.getId());
            em.remove(findCategory);


            transaction.commit();
        } catch (Exception e) {
            transaction.rollback();
            e.printStackTrace();
        } finally {
            em.close();
        }

        emf.close();
    }
}

 

실행결과

  • Collection이 제거되었기 때문에 모두 삭제한다.