آن چه در این مقاله خواهید خواند:
کش در Hibernate چگونه پیاده سازی میشود؟
من ارسلان میربزرگی، در این مقاله قصد دارم تا شما را با مفهوم Hibernate در جاوا و استفاده از کش در آن آشنا کنم. در دوره های قبلی برنامه نویسی جاوا، با مفاهیم پایه مربوط به جاوا آشنا شدید و اکنون مفاهیم جدیدتری را در این مقاله برای شما مطرح خواهم کرد.

Caching در Hibernate
یکی از قابلیتهای دیگری که توسط Hibernate ارائه میشود، پشتیبانی از Caching است. با استفاده از این قابلیت، ورود درخواستها به بانک اطلاعاتی کاهش مییابد و کارایی برنامه افزایش پیدا میکند. Cache بین برنامه و بانک اطلاعاتی قرار میگیرد و مراجعات مکرر به بانک اطلاعاتی را تا حد امکان کاهش میدهد. به طور خلاصه میتوان چنین گفت که Cache در Hibernate با کاهش ورود درخواستها به بانک اطلاعاتی، باعث افزایش کارایی برنامه میشود.

Cache در لایه سطح دوم Hibernate
در شکل زیر مشاهده میکنید که Cache در Hibernate در 2 سطح به کار گرفته میشود. هدف از اراده این مقاله نیز، بررسی Cache در سطح دوم Hibernate است. به این منظور پس از ارائه توضیحات بیشتر، ابتدا مقدمه کوتاهی در مورد Cache در سطح اول Hibernate به شما ارائه میدهیم و پس از آن در مورد Cache در سطح دوم Hibernate صحبت خواهیم کرد.
کش سطح اول یا First level Cache در Hibernate
Cache در سطح اول، در سطح یک session فعال است و در تمامی درخواستهایی که رد و بدل می شوند استفاده میشود. این سطح از Cache ، به طور خودکار، تمامی درخواست را قبل از فرستادن به سمت بانک اطلاعاتی، پردازش می کند و از تبادل تراکنش تکراری با بانک اطلاعاتی جلوگیری میکند. ویژگی های Cache در سطح اول را میتوان در موارد زیر خلاصه کرد:
- این سطح از Cache به طور پیش فرض، در سطح یک session فعال است.
- نیاز به اعمال تنظیمات خاصی از طرف برنامه نویسان ندارد.
- برنامه نویسان قادر به غیرفعال کردن این سطح نیستند.
- این Cache در تمام طول کارکرد session در دسترس است.
- اگر session غیرفعال شود، تمام اشیاء کش شده نیز از بین خواهند رفت.
- اشیاء کش شده نمی توانند بین چند session به اشتراک گذاشته شوند.
در شکل زیر، مراحل بارگذاری یک شی را مشاهده میکنید. در این شکل، Hibernate پس از گرفتن درخواست، ابتدا موجود بودن شی درخواستی بر روی session را بررسی میکند. در صورت موجود بودن این شی، دیتا از session بازخوانی شده و برگردانده میشود. در صورتی که شی موجود نباشد، درخواست به سمت بانک اطلاعاتی رفته و دادهها از بانک اطلاعاتی استخراج میشوند. در این حالت Cache نیز ساخته خواهد شد.

کش سطح دوم یا Second level Cache در Hibernate
Cache در سطح دوم، به شی Session Factory وابسته است و در هنگام اجرای تراکنش ها، تمامی اشیاء را در سطح Factory بارگذاری میکند. در نتیجه، این اشیاء به یک کاربر محدود نبوده و در تمام سطح برنامه در دسترس خواهند بود. ویژگی های Cache در سطح دوم را می توان در موارد زیر خلاصه کرد:
- این سطح از Cache به طور پیش فرض، در سطح شی Session Factory وجود دارد.
- نیاز به اعمال تنظیمات خاصی از طرف برنامه نویسان دارد.
- استفاده از این کش اختیاری است.
- برای این کش، پیاده سازی های مختلفی مانند EH Cache, OS Cache, SWARM Cache, JBOSS Cache وجود دارد.
مراحل بارگذاری با این کش، مشابه کش سطح اول است. تنها تفاوت آنها در این است که اگر داده مورد نظر در سطح Session پیدا نشود، جستجو در سطح Session Factory که فراگیرتر است انجام می شود، و در صورتی که جستجو در Session Factory نیز نتیجه ای در برنداشت، درخواست به بانک اطلاعاتی ارسال میشود.

مثال :
میخواهیم پیاده سازی کش سطح دوم با استفاده از EH Cache را مورد بررسی قرار دهیم. در این برنامه کش سطح دوم Hibernate را فعال کرده و از آن استفاده میکنیم. پس از آن، dependency تحت عنوان Hibernate -ehcache را به pom پروژه اضافه میکنیم.

در تنظیمات Hibernate نیز باید تغییراتی انجام دهیم. با فرض singleton بودن factory مقدار factory class را برابر singleton EH Cache Reign Factory قرار میدهیم و کش سطح دوم و کش query را فعال کرده و تنظیمات اختصاصی EH Cache را نیز باید به صورت یک فایل مجزا xml به Hibernate معرفی کنیم.

حالا در فایل تنظیمات ( ehcache.xml) باید این موارد تعیین شوند.
- در تگ
مسیری برای نگهداری محتوای cache شده تعیین میشود. در واقع این cache در حافظه اصلی انجام میشود و این مسیر، جایی است که در صورت overflow یا سرریز شدن دیتاها از آن استفاده میشود. - در تگ بعدی یعنی تگ
برای cache تمامی entity ها، تنظیمات پیش فرض یکسانی اعمال میشود اما اگر یک entity بخواهد تنظیمات متفاوتی را داشته باشد، باید این تنظیمات اختصاصی در یک تگ اختصاصی، به Hibernate معرفی شود.
در عنصر persistence، استراتژی cache برابر Local Temp Swap تعیین شده است. به این ترتیب Hibernate از حافظه موقتی استفاده میکند و به طور خودکار با ریسِت شدن برنامه، خالی میشود.

در هر بخشی که قرار است کش روی آن اعمال شود یک انوتیشن @cache قرار می گیرد که در آن، علاوه بر اعلام استراتژی کش، قسمتی از فایل تنظیمات ( ehcache.xml) برای اعمال تنظیمات معرفی میشود.


در این مرحله به سراغ مراحل اجرایی برنامه میرویم. به این منظور، کد مربوط به 2 شی را در شرایط مختلف بارگذاری میکنیم اما به صورت همزمان، 2 session مجزا ( session B ، session A ) را نیز بر روی Session Factory باز میکنیم. Session Factory دارای متدی به نام get Statistics () است. این متد حاوی اطلاعات آماری در زمینه تمامی وقایعی است که ممکن است روی یک Session Factory رخ بدهد.

برای استفاده از این اطلاعات، باید آنها را فعال کرد. 3 مورد از این اطلاعات در زمینه کش سطح دوم هستند و در متد print Date() که در این کلاس ذکر کردیم، استفاده شدهاند.
متد get Second Level Cache Hit Count() ، تعداد دفعات استخراج موقت یک entity از کش را بازمیگرداند. زمانی که یک رکورد درخواست می شود، ابتدا به کش رجوع می شود و اگر رکورد مورد نظر در کش وجود داشته باشد، از آن استفاده میشود و یک واحد هم به این عدد افزوده میشود.
متد get Second Level Cache Miss Count() ، تعداد دفعات مراجعه ناموفق به به کش را برمیگرداند. به عبارتی این متد، بیانگر تعداد دفعاتی است که مراجعه به کش انجام شده است اما رکورد مورد نظر در کش وجود نداشت است.
متد get Second Level Cache Put Count() ، تعداد دفعاتی که entity در کش قرار داده شده را برمیگرداند. این به این معنا است که زمانی که یک entity از بانک استخراج میشود، نسخهای از آن در کش قرار گرفته و این عدد یک واحد افزایش مییابد.

متد دیگری نیز به نام متد get Entity Fetch Count در اینجا استفاده شده است. این متد، به تعداد دفعات ارسال درخواست Fetch به بانک اطلاعاتی اشاره دارد.

روند اجرای برنامه را از نقطه تعیین شده با breakpoint بررسی میکنیم. رکوردی از جدول Employee را از قسمت session A لود می کنیم. سپس با متدی که قبلا نوشتهایم، محتوای رکورد را استخراج میکنیم و اطلاعات آماری مربوط به کش را چاپ میکنیم. چاپ متن query در کنسول به معنای استخراج مستقیم اطلاعات از بانک است. همانطور که در اطلاعات آماری نیز این نکته که درخواست به سمت بانک ارسال شده است، قابل مشاهده است. تعداد دفعات برداشت از کش یا Hibernate برابر با صفر است. زیرا تا قبل از این، چنین رکوردی در کش موجود نبوده است. تعداد دفعات مراجعه ناموفق به کش نیز برابر 1 و تعداد دفعات ذخیره شدن در کش هم برابر 1 می شود. زیرا از این لحظه به بعد این رکورد در کش ذخیره میشود.

در مرحله دوم، مجددا همان رکورد مرحله اول را لود میکنیم. تعداد دفعات استخراج از بانک، تغییری نمیکند چون این رکورد قبلا لود شده است و در کش موجود است. در واقع هیچ عملیاتی انجام نمیشود و در پارامترهای مربوط به کش هم تغییری رخ نمیدهد.

در مرحله سوم، توسط دستور evict، entity لود شده را از روی session حذف میکنیم. در کش سطح اول، توسط چنین دستوری، کش سطح اول که کش پیش فرض و در سطح session است، پاک میشود اما در اینجا با حذف entity از روی session ، کش سطح دوم که در سطح Session Factory قرار دارد، پابرجا خواهد ماند. به اطلاعات چاپ شده دقت کنید. این اطلاعات، تفاوتی با مرحله قبلی ندارند و در واقع با از بین رفتن اطلاعات موجود در session، اطلاعات کش سطح دوم از بین نخواهد رفت.

در مرحله بعدی، رکورد جدیدی را روی این session لود میکنیم. مشاهده میکنیم که تعداد دفعات استخراج به میزان یک واحد اضافه میشود. در مقابل، تعداد دفعات برداشت از کش، تغییری نمیکند و تعداد دفعات مراجعات ناموفق نیز افزایش یافت. همچنین تعداد دفعات قرار گرفتن رکورد در کش نیز به میزان یک واحد افزایش یافت زیرا رکورد جدید، از این لحظه به بعد در کش قرار گرفته است.

در مرحله آخر نیز، همان رکورد اولیه را این بار در یک session جدید لود میکنیم. با توجه به مشترک بودن کش در سطح session ها، درخواست به سمت بانک اطلاعاتی ارسال نشده و رکورد از cache برداشته میشود.

بنابراین تعداد دفعات استخراج از کش افزایش یافته اما تعداد دفعات مراجعه ناموفق و همچنین تعداد دفعات ذخیره شدن، تغییری نمیکند.