JPA Cascade Types | انواع Cascade در JPA
📖توضیحات درس
📄جزوه
مفاهیم کلیدی
- Cascade
در JPA، Cascade یعنی وقتی روی یک Entity والد عملیاتی انجام میدهید، همان عملیات به صورت خودکار روی Entityهای وابسته نیز اعمال شود. این قابلیت برای مدیریت صحیح روابطی مثل OneToMany و ManyToOne بسیار مهم است.
- وابستگی بین Entityها
برخی Entityها به صورت مفهومی وابسته به Entity دیگری هستند. مثلاً Address بدون Person معمولاً معنای مستقلی در مدل دامنه ندارد. در چنین شرایطی Cascade کمک میکند عملیات والد به فرزند نیز منتقل شود.
- CascadeType
در JPA، انواع مختلفی از CascadeType تعریف شدهاند که مشخص میکنند چه عملیاتی از والد به فرزند propagate شود. انواع استاندارد JPA شامل ALL، PERSIST، MERGE، REMOVE، REFRESH و DETACH هستند.
- CascadeType.ALL
این گزینه باعث میشود تمام عملیاتهای اصلی چرخه حیات Entity از والد به فرزند منتقل شوند. یعنی اگر روی والد persist، merge، remove، refresh یا detach انجام شود، همان عملیات روی فرزند نیز اعمال خواهد شد.
این گزینه زمانی مناسب است که چرخه حیات فرزند کاملاً وابسته به والد باشد.
- CascadeType.PERSIST
اگر والد با persist() ذخیره شود، فرزند نیز به صورت خودکار ذخیره میشود. این حالت برای زمانی کاربرد دارد که یک Entity جدید به همراه فرزندان جدیدش ساخته شده و باید همه با هم وارد دیتابیس شوند.
- CascadeType.MERGE
اگر یک Entity والد detached باشد و با merge() دوباره به Persistence Context متصل شود، وضعیت فرزندان آن نیز merge میشود.
این عملیات معمولاً ابتدا وضعیت موجود را از دیتابیس میخواند و سپس تغییرات را روی نمونه managed اعمال میکند.
- CascadeType.REMOVE
اگر والد حذف شود، فرزندهای وابسته نیز حذف میشوند. این نوع cascade زمانی مفید است که حذف والد باید منجر به حذف کامل دادههای وابسته شود.
- CascadeType.DETACH
اگر والد از Persistence Context خارج شود، فرزندان آن نیز از context خارج میشوند. در نتیجه دیگر توسط EntityManager track نخواهند شد.
- CascadeType.REFRESH
اگر والد با refresh() دوباره از دیتابیس بارگذاری شود، فرزندان آن نیز refresh میشوند. این کار برای بازگرداندن تغییرات local و همگامسازی مجدد با دیتابیس کاربرد دارد.
- Persistence Context
محیطی است که EntityManager در آن Entityها را مدیریت میکند. وقتی یک Entity داخل این context باشد، تغییراتش track میشود. رفتار Cascade مستقیماً با همین context و عملیات چرخه حیات مرتبط است.
- Parent-Child Relationship
در روابطی مانند @OneToMany و @ManyToOne معمولاً Cascade روی سمت والد تعریف میشود تا عملیات والد به collection فرزندان منتقل شود.
مثال:
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
private List<Address> addresses;
- جهت اعمال Cascade
Cascade از سمتی اعمال میشود که آن را تعریف کردهاید. اگر روی والد تعریف شود، عملیات والد به فرزند propagate میشود؛ نه برعکس.
- Transient to Persistent Transition
یکی از رایجترین کاربردهای Cascade زمانی است که یک والد و فرزند هر دو جدید هستند. با CascadeType.PERSIST فقط کافی است والد را persist کنید تا فرزندها هم persistent شوند.
- Detached Entity Merge Behavior
در merge()، JPA معمولاً نمونه detached را مستقیماً managed نمیکند، بلکه state آن را به یک نمونه managed با همان شناسه منتقل میکند. اگر cascade فعال باشد، همین منطق برای فرزندها نیز اجرا میشود.
موارد مصاحبه ای
- Cascade در JPA چیست؟
مکانیزمی است که باعث میشود عملیات چرخه حیات Entity مانند persist، merge، remove و refresh از والد به Entityهای مرتبط منتقل شود.
- تفاوت
CascadeType.PERSISTوCascadeType.MERGEچیست؟
PERSIST برای ذخیره Entityهای جدید استفاده میشود، اما MERGE برای همگامسازی وضعیت یک Entity detached با نمونه managed در Persistence Context به کار میرود.
- چه زمانی از
CascadeType.ALLاستفاده میکنیم؟
وقتی چرخه حیات فرزند کاملاً به والد وابسته باشد و بخواهیم تمام عملیات اصلی به صورت خودکار به فرزند نیز اعمال شوند.
- اگر
CascadeType.REMOVEفعال باشد چه اتفاقی میافتد؟
با حذف Entity والد، Entityهای فرزند مرتبط نیز حذف میشوند.
- آیا
Cascadeروی هر دو سمت رابطه اعمال میشود؟
خیر، فقط از سمتی اعمال میشود که در annotation تعریف شده است.
- تفاوت
CascadeType.REMOVEبا حذف دستی فرزندها چیست؟
در REMOVE، حذف فرزندها به صورت خودکار و declarative انجام میشود؛ اما در حذف دستی، توسعهدهنده باید خودش ترتیب و منطق حذف را مدیریت کند.
CascadeType.DETACHچه کاربردی دارد؟
زمانی که بخواهیم والد و فرزندانش همزمان از Persistence Context خارج شوند تا دیگر تغییرات آنها track نشود.
refresh()همراه با cascade چه کاری انجام میدهد؟
وضعیت والد و فرزندها را از روی دیتابیس دوباره بارگذاری میکند و تغییرات local ذخیرهنشده را از بین میبرد.
- آیا
Cascadeبه معنی fetch کردن خودکار است؟
خیر، Cascade مربوط به propagation عملیات چرخه حیات است، در حالی که fetch نحوه بارگذاری داده را مشخص میکند.
- اشتباه رایج در استفاده از
CascadeType.ALLچیست؟
استفاده بدون تحلیل مدل دامنه؛ چون ممکن است باعث حذف یا بهروزرسانی ناخواسته Entityهای وابسته شود.
سناریو کاربردی
فرض کنید یک Person دارای چند Address است و در مدل دامنه، آدرسها بخشی از چرخه حیات شخص محسوب میشوند. در این حالت روی رابطه OneToMany از CascadeType.PERSIST یا CascadeType.ALL استفاده میکنیم.
@Entity
public class Person {
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
private List<Address> addresses;
}
اکنون اگر یک Person جدید همراه با یک Address جدید بسازیم و فقط والد را ذخیره کنیم:
entityManager.persist(person);
JPA به صورت خودکار فرزندها را نیز ذخیره میکند.
همین منطق برای حذف نیز برقرار است؛ اگر CascadeType.REMOVE یا ALL فعال باشد، با حذف Person، تمام Addressهای وابسته نیز حذف میشوند.
در سناریوی update نیز اگر Person و Address detached باشند و والد را merge() کنیم، تغییرات فرزند هم به صورت خودکار merge میشود. این رفتار باعث میشود مدیریت graph شیءها سادهتر شود، به شرطی که وابستگی دامنه درست طراحی شده باشد.
بیشتر بدانید
- Cascade با Relationship Design
قبل از انتخاب CascadeType باید مشخص کنید آیا فرزند واقعاً مالک چرخه حیات مستقل است یا نه. اگر مستقل باشد، استفاده از CascadeType.ALL ممکن است خطرناک باشد.
- Cascade با
fetchمتفاوت است
Cascade تعیین میکند چه عملیاتی propagate شوند، اما FetchType.LAZY و FetchType.EAGER فقط نحوه لود شدن داده را کنترل میکنند.
- انتخاب حداقلی بهتر از انتخاب حداکثری است
در بسیاری از پروژهها بهتر است به جای CascadeType.ALL فقط cascadeهای موردنیاز مثل PERSIST و MERGE را فعال کنید.
- در روابط حساس روی
REMOVEدقت کنید
اگر فرزندها shared باشند یا در بخشهای دیگر سیستم هم استفاده شوند، CascadeType.REMOVE میتواند باعث حذف ناخواسته دادهها شود.
- الگوی رایج در پروژههای واقعی
برای aggregateهایی که فرزند بدون والد معنای مستقلی ندارد، ترکیب CascadeType.PERSIST و CascadeType.MERGE بسیار رایج و امنتر از استفاده پیشفرض از ALL است.
