🌱 Entity의 μƒνƒœ (생λͺ… μ£ΌκΈ°)

JPAμ—μ„œ μ—”ν‹°ν‹°λŠ” 크게 4가지 μƒνƒœλ₯Ό 가진닀.

 

1. Transient (λΉ„μ˜μ†)

μ—”ν‹°ν‹° 객체가 Java λ©”λͺ¨λ¦¬μ—μ„œλ§Œ μ‘΄μž¬ν•˜κ³ , μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ™€ 아무 κ΄€λ ¨ μ—†λŠ” μƒνƒœ

Person person = new Person();

 

2. Persistent (μ˜μ†)

μ—”ν‹°ν‹° 객체가 μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ— κ΄€λ¦¬λœ μƒνƒœ

μ˜μ† μƒνƒœλΌκ³  λ°”λ‘œ λ°μ΄ν„°λ² μ΄μŠ€μ— 쿼리가 λ‚ λΌκ°€λŠ” 것은 μ•„λ‹ˆκ³  주둜 νŠΈλžœμž­μ…˜μ΄ commit λ˜κ±°λ‚˜  flush λ©”μ„œλ“œ μ‹€ν–‰ μ‹œ λ°μ΄ν„°λ² μ΄μŠ€μ— 반영 λœλ‹€.

entityManger.persist(person);

 

3. Detached (μ€€μ˜μ†)

μ—”ν‹°ν‹° 객체가 μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ— μ €μž₯λ˜μ—ˆλ‹€κ°€ λΆ„λ¦¬λœ μƒνƒœ

더이상 μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ— μ˜ν•΄ κ΄€λ¦¬λ˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— 1μ°¨ μΊμ‹œ λ“±μ—μ„œ λͺ¨λ“  정보가 μ‚­μ œλœλ‹€.

entityManger.detach(person);

 

4. Removed (μ‚­μ œ)

μ—”ν‹°ν‹°λ₯Ό μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ™€ λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ μ‚­μ œν•œ μƒνƒœ

entityManger.remove(person);

 

Cascade μ˜΅μ…˜μ€ μ΄λŸ¬ν•œ μƒνƒœ λ³€ν™”λ₯Ό μ—°κ΄€λœ 엔티티에 μ „μ΄μ‹œν‚€λŠ” μ˜΅μ…˜μ΄λ‹€.

 

🌱 Cascade λž€?

CascadeλŠ” JPA(Java Persistence API)μ—μ„œ 연관관계λ₯Ό 가진 μ—”ν‹°ν‹°μ˜ 생λͺ… μ£ΌκΈ°λ₯Ό κ΄€λ¦¬ν•˜κΈ° μœ„ν•΄ μ‚¬μš©λ˜λŠ” 섀정이닀.

이 μ˜΅μ…˜μ„ μ„€μ •ν•˜λ©΄ λΆ€λͺ¨ μ—”ν‹°ν‹°μ˜ μƒνƒœ λ³€κ²½μ΄λ‚˜ μ‚­μ œκ°€ μžμ‹ μ—”ν‹°ν‹°μ—κ²Œλ„ 영ν–₯을 미치게 λœλ‹€.

 

리뷰(Review)와 리뷰 이미지(ReviewImage)λ₯Ό μ˜ˆμ‹œλ‘œ 듀어보면

@Entity
public class Review {

    @Id
    @GeneratedValue
    private Long id;
    
    private String content;
    
    @OneToMany(mappedBy = "review")
    private List<ReviewImage> reviewImages = new ArrayList<>();
    
    // constructor, getter, setter, ...
}
@Entity
public class ReviewImage {

    @Id
    @GeneratedValue
    private Long id;

    private String imageUrl;

    @ManyToOne
    @JoinColumn(name = "review_id")
    private Review review;
    
    // constructor, getter, setter, ...
}
@Test
public void no_cascade() {

    Review review = new Review();
    review.setContent("리뷰 λ‚΄μš©");
    
    ReviewImage reviewImage1 = new ReviewImage();
    reviewImage1.setImageUrl("리뷰이미지1 url");
    ReviewImage reviewImage2 = new ReviewImage();
    reviewImage2.setImageUrl("리뷰이미지2 url");
    
    review.getReviewImageList().add(reviewImage1);
    review.getReviewImageList().add(reviewImage2);
    
    // μ˜μ†ν™” μž‘μ—… μˆ˜ν–‰
    entityManager.persist(review);
    entityManager.persist(reviewImage1);
    entityManager.persist(reviewImage2);
}

Review 엔티티와 ReviewImage μ—”ν‹°ν‹°λŠ” μ–‘λ°©ν–₯ 연관관계가 λ§Ίμ–΄μ Έμžˆλ‹€.

λ§ˆμ§€λ§‰ ν…ŒμŠ€νŠΈ μ½”λ“œμ—μ„œ λ§Œλ“  [review, reviewImage1, reviewImage2] μ„Έ μΈμŠ€ν„΄μŠ€λ₯Ό μ˜μ†ν™”ν•˜κΈ° μœ„ν•΄μ„œλŠ” entityManager.persist λ©”μ„œλ“œλ₯Ό 3번 ν˜ΈμΆœν•΄ Review와 ReviewImageλ₯Ό 각각 μ˜μ†ν™” μ‹œμΌœμ£ΌλŠ” μž‘μ—…μ„ 진행해야 ν•œλ‹€.

 

μ—¬κΈ°μ„œ Reviewκ³Ό κ΄€λ¦¬ν•˜λŠ” ReviewImage μΈμŠ€ν„΄μŠ€μ˜ 경우 Reviewλ₯Ό persist ν•˜λ©΄ ReviewImage μžλ™μœΌλ‘œ persist 해쀄 수 μžˆλŠ” μ˜΅μ…˜μ΄ Cascade이닀.

 

🌱 Cascade μ˜΅μ…˜

- PERSIST

- MERGE

- REMOVE

- REFRESH

- DETATCH

- ALL

 

리뷰(Review)와 리뷰 이미지(ReviewImage)λ₯Ό μ˜ˆμ‹œλ‘œ 듀어보면

@Entity
public class Review {

    @Id
    @GeneratedValue
    private Long id;
    
    private String content;
    
    @OneToMany(mappedBy = "review")
    private List<ReviewImage> reviewImages = new ArrayList<>();
    
    // constructor, getter, setter, ...
}
@Entity
public class ReviewImage {

    @Id
    @GeneratedValue
    private Long id;

    private String imageUrl;

    @ManyToOne
    @JoinColumn(name = "review_id")
    private Review review;
    
    // constructor, getter, setter, ...
}

 

🫧 CascadeType.PERSIST

ν•˜μœ„ μ—”ν‹°ν‹°κΉŒμ§€ μ˜μ†μ„±μ„ μ „λ‹¬ν•œλ‹€.

Review μ—”ν‹°ν‹°λ₯Ό μ €μž₯ν•˜λ©΄ ν•˜μœ„ 엔티티인 ReviewImage 엔티티도 μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ— μ €μž₯ν•œλ‹€.

@Test
public void persist_test() {

    Review review = new Review();
    review.setContent("리뷰 λ‚΄μš©");
    
    ReviewImage reviewImage1 = new ReviewImage();
    reviewImage1.setImageUrl("리뷰이미지1 url");
    ReviewImage reviewImage2 = new ReviewImage();
    reviewImage2.setImageUrl("리뷰이미지2 url");
    
    review.getReviewImageList().add(reviewImage1);
    review.getReviewImageList().add(reviewImage2);
    
    // μ˜μ†ν™” μž‘μ—… μˆ˜ν–‰
    entityManager.persist(review);
    entityManager.flush();
}

review μ—”ν‹°ν‹°λ₯Ό μ˜μ†ν™”ν•˜λ©΄ PERSIST μ˜΅μ…˜μœΌλ‘œ 인해 [reviewImage1, reviewImage2]도 μ˜μ†ν™” λœλ‹€.

 

🫧 CascadeType.MERGE

ν•˜μœ„ μ—”ν‹°ν‹°κΉŒμ§€ 병합 μž‘μ—…μ„ μ§€μ†ν•œλ‹€.

Review 엔티티와 ReviewImage μ—”ν‹°ν‹°λ₯Ό μ‘°νšŒν•΄μ™€ μˆ˜μ •ν•œ ν›„ Review μ—”ν‹°ν‹°λ₯Ό Mergeν•˜λ©΄ ν•˜μœ„ 엔티티인 ReviewImage μ—”ν‹°ν‹°μ˜ λ³€κ²½ 사항도 μˆ˜μ •λœλ‹€.

@Test
public void merge_test() {

    Review review = new Review();
    review.setContent("리뷰 λ‚΄μš©");
    
    ReviewImage reviewImage1 = new ReviewImage();
    reviewImage1.setImageUrl("리뷰이미지1 url");
    ReviewImage reviewImage2 = new ReviewImage();
    reviewImage2.setImageUrl("리뷰이미지2 url");
    
    review.getReviewImageList().add(reviewImage1);
    review.getReviewImageList().add(reviewImage2);
    
    // μ˜μ†ν™” μž‘μ—… μˆ˜ν–‰ 및 μ˜μ†μ„± μ»¨ν…μŠ€νŠΈ μ΄ˆκΈ°ν™”
    entityManager.persist(review);
    entityManager.flush();
    entityManager.clear();
    
    Review savedReview = entityManager.find(0L, Review.class);
    ReviewImage savedReviewImage = entityManager.find(0L, ReviewImage.class);
    
    // μ—”ν‹°ν‹° μˆ˜μ •
    savedReview.setContent("리뷰 λ‚΄μš© μˆ˜μ •");
    savedReviewImageUrl.setImageUrl("리뷰이미지1 url μˆ˜μ •");
    
    // merge μž‘μ—… μˆ˜ν–‰
    entityManager.merge(savedReview);
    entityManager.flush();
}

review μ—”ν‹°ν‹° merge μ‹œ MERGE μ˜΅μ…˜μœΌλ‘œ 인해 reviewImage1도 merge λœλ‹€.

 

🫧 CascadeType.REMOVE

ν•˜μœ„ μ—”ν‹°ν‹°κΉŒμ§€ μ‚­μ œ μž‘μ—…μ„ μ§€μ†ν•œλ‹€.

Review μ—”ν‹°ν‹°λ₯Ό μ‚­μ œν•˜λ©΄ ν•˜μœ„ 엔티티인 ReviewImage 엔티티도 μ‚­μ œλœλ‹€.

@Test
public void remove_test() {

    Review review = new Review();
    review.setContent("리뷰 λ‚΄μš©");
    
    ReviewImage reviewImage1 = new ReviewImage();
    reviewImage1.setImageUrl("리뷰이미지1 url");
    ReviewImage reviewImage2 = new ReviewImage();
    reviewImage2.setImageUrl("리뷰이미지2 url");
    
    review.getReviewImageList().add(reviewImage1);
    review.getReviewImageList().add(reviewImage2);
    
    // μ˜μ†ν™” μž‘μ—… μˆ˜ν–‰ 및 μ˜μ†μ„± μ»¨ν…μŠ€νŠΈ μ΄ˆκΈ°ν™”
    entityManager.persist(review);
    entityManager.flush();
    entityManager.clear();
    
    Review savedReview = entityManager.find(0L, Review.class);
    
    // remove μž‘μ—… μˆ˜ν–‰
    entityManager.remove(savedReview);
    entityManager.flush();
}

review μ—”ν‹°ν‹° remove μ‹œ REMOVE μ˜΅μ…˜μœΌλ‘œ 인해 [reviewImage1, reviewImage2]도 remove λœλ‹€.

 

🫧 CascadeType.REFRESH

μ˜μ†λœ μƒνƒœμ—μ„œ μ‘°νšŒν•œ μ—”ν‹°ν‹°κ°€ λ³€κ²½λ˜λ”λΌλ„, refresh() ν•΄μ„œ λ°μ΄ν„°λ² μ΄μŠ€μ— μ €μž₯된 값을 override ν•˜κ³ , μžμ‹ 엔티티도 refresh ν•œλ‹€.

β†’ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ— μ €μž₯된 κ°’ μƒˆλ‘œκ³ μΉ¨

Review μ—”ν‹°ν‹°λ₯Ό Refreshν•˜λ©΄ ν•˜μœ„ 엔티티인 ReviewImage 엔티티도 Refreshλœλ‹€.

 

🫧 CascadeType.DETACH

μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ—μ„œ 더이상 ν•΄λ‹Ή μ—”ν‹°ν‹°λ₯Ό κ΄€λ¦¬ν•˜μ§€ μ•ŠλŠ” μ€€μ˜μ† μƒνƒœλ‘œ λ§Œλ“ λ‹€.

Review μ—”ν‹°ν‹°λ₯Ό Detachν•˜λ©΄ ν•˜μœ„ 엔티티인 ReviewImage 엔티티도 Detachλœλ‹€.

 

🫧 CascadeType.ALL

μœ„μ˜ μ˜΅μ…˜λ“€μ΄ λͺ¨λ‘ 적용된 μƒνƒœλ‘œ, μƒμœ„ μ—”ν‹°ν‹°μ˜ λͺ¨λ“  μƒνƒœ λ³€ν™” μž‘μ—…μ΄ ν•˜μœ„ 엔티티에 μ „νŒŒλœλ‹€.

 

 

 

 

JPA Cascade Type : PERSIST vs MERGE (Why choose what?)

주문에 λŒ€ν•œ 둜직 μˆ˜μ • 쀑, Detached Entity Passed to Persistλ₯Ό λ§Œλ‚˜λ³΄μ•˜λ‹€.우리의 엔티티에 λŒ€ν•΄μ„œ λŒμ•„λ³΄κ²Œ λ˜μ—ˆλ‹€.그러던 쀑 μš°λ¦¬κ°€ μ™œ MERGE λŒ€μ‹  PERSIST λ₯Ό μ‚¬μš©ν•˜κ²Œ λ˜μ—ˆλŠ”μ§€ 기얡이 μ•ˆ 났닀.μ–΄λ–€ μ°¨

velog.io

 

[jpa] CascadeType.PERSISTλ₯Ό ν•¨λΆ€λ‘œ μ‚¬μš©ν•˜λ©΄ μ•ˆλ˜λŠ” 이유

μ—”ν‹°ν‹°μ˜ μžμ‹μ— CascadeType.PERSISTλ₯Ό 지정할 경우 JPAμ—μ„œ μΆ”κ°€μ μœΌλ‘œ μˆ˜ν–‰ν•˜λŠ” λ™μž‘μ΄ 있고, 이 λ•Œλ¬Έμ— μ˜ˆμƒμΉ˜ λͺ»ν•œ μ‚¬μ΄λ“œ μ΄νŽ™νŠΈκ°€ λ°œμƒν•  수 μžˆμœΌλ―€λ‘œ 이λ₯Ό λ‚¨κ²¨λ‘κ³ μž ν•œλ‹€. 일단 기본적으둜 c

joont92.github.io

 

<JPA> μ•Œκ³  μ“°λŠ” Cascade(μ˜μ†μ„± 전이)

이번 ν¬μŠ€νŒ…μ—μ„œλŠ” μ“°λ©΄μ„œλ„ ν—·κ°ˆλ¦¬λ˜ JPA의 Cascade μ˜΅μ…˜μ— λŒ€ν•΄μ„œ μ •λ¦¬ν•˜κ² μŠ΅λ‹ˆλ‹€.일단 기본적으둜 CascadeλΌλŠ” μ˜΅μ…˜μ΄ λ“±μž₯ν•˜κ²Œ 된 λ°°κ²½λΆ€ν„° μ•Œμ•„λ΄…μ‹œλ‹€. μ•„λž˜μ˜ μ½”λ“œλ₯Ό λ³΄μ‹œμ£  @Setter @Getter @Entity

hongchangsub.com

 

JPA cascade 멈좰!

1) μ„œλ‘  업무 쀑 μˆ˜μ‹­ 개의 ν…Œμ΄λΈ”μ— 걸친 데이터λ₯Ό ν•œ λ²ˆμ— μ§€μ›Œμ•Ό ν•˜λŠ” APIλ₯Ό λ§Œλ“€μ–΄μ•Ό ν–ˆμŠ΅λ‹ˆλ‹€. μ²˜μŒμ—λŠ” κ³ μž‘ CRUD인데, 금방 ν•˜κ² μ§€λΌλŠ” λ§ˆμŒμ„ 가지고 μ‹œμž‘ν–ˆμŠ΅λ‹ˆλ‹€. μ΄λ•Œ 생각지도 λͺ» ν•œ μ–΄

yeon-kr.tistory.com