یادداشتهایی بر رهیافت ORM با تاکید بر Hibernate – بخش چهارم
مطالب مورد بررسی دراین مقاله :مفهوم Fine-grained domain modelروشهای شناسایی entity ها و value type ها در یک domain model
بیشتر بخوانیدمعمولاً در اکثر پروژه های مبتنی بر db فرایند تولید مقدار برای ویژگی id را خودکار در نظر میگیرند مگر در موقعیتهای استثنایی که ما مجبور به دستی ست کردن مقدار برای فیلد id باشیم ( یکی از موارد کاربرد میتواند به هنگام کار با سیستمهای قدیمی باشد. )
موقعی که بخواهیم یک entity را ذخیره کنیم ، معمولاً از سیستم میخواهیم برای فیلد id یک مقدار را تولید کند . بنابراین روی فیلدی که به عنوان id در نظر گرفتیم ( معمولاً با علامت @Id مشخص میشود ) به سیستم با گذاشتن علامت @GeneratedValue روی همان فیلد اعلام میکنیم زحمت تولید مقدار برای فیلد Id با تو هست .
در این مقاله در مورد مطالب زیر صحبت خواهیم کرد :
روشهای تولید id در JPA
در این بخش در مورد روشها و استراتژی های تولید id در JPA صحبت می کنیم . ابتدا در مورد استراتژی های تولید id در JPA مزایا و معایب هر یک صحبت می کنیم در مقاله بعدی سراغ استراتژی های Hibernate می رویم .
قبل از اینکه سراغ معرفی استراتژی های تولید id برویم بهتر است چند نکته را مدنظر قرار داشته باشیم :
یک – بخاطر مسایل بهینه سازی ، سیاست Hibernate اغلب این است که عمل درج (insert) موجودیت ها تا جایی که می تواند به تعویق بیندازد و عمل درج را به صف برده و در نهایت همه درخواستهای insert را به صورت یکجا و دسته ای ( batch) انجام دهد .
دو – بخاطر در پیش گرفتن سیاست ذکر شده در بند یک ترجیح ما (برنامه نویسان ) این است که مقدار id حتی قبل از عمل insert واقعی در DB تولید و در دست ما باشد .
سه – در اغلب روشها ما با مفهومی بنام Sequence سروکار داریم که یا در سمت DB توسط DBMS هندل می شود یا توسط Hibernate . در واقع Sequence یک ویژگی است در دنیای DB . که وظیفه اش تولید مقادیر منحصربفرد برای ستون های Primary key در جدول های یک DB است . نکته مهمی که در مورد Sequence باید بهش توجه کنیم این است که فرایند تولید مقادیر id در اغلب DBMSها خارج از تراکنش رخ می دهد به عبارت دیگر transaction-less است یعنی یک مقدار تولید شده نمی تواند همزمان به چندین تراکنش همروند در حال اجرا منتسب بشود . مطلب بعدی در مورد Sequence این است که مقداری که برای یک id تولید می شود اگر به یک تراکنش خاصی منتسب بشود چه اون تراکنش commit گردد و چه rollback بشود مقدار منتسب شده ازش در جای دیگر استفاده نمی شود .
بخاطر مطالب گفته شده در بندهای یک و دو ترجیح ما در انتخاب استراتژی تولید id این است که نیاز ما برنامه نویسان از نظر مطالب گفته شده در بند دو را تامین کند .
در JPA چهار روش برای تولید مقدار برای id وجود دارد :
در ادامه هر یک را به طور خلاصه بررسی می کنیم :
روش Idnetity :
این روش توسط محصولات زیر پشتیبانی می شود :
دراین روش مقدار منتسب شده به id برای entity ی که می خواهد در جدول درج بشود تا زمان درج مشخص نیست به عبارت بهتر اول باید عمل درج در جدول رخ دهد بعد مقدار id بدست آید در این حالت اگر در سطح app بخواهیدبلافاصله بعد از عمل درج عمل زیر را برای بازیابی مقدار id انجام دهید ممکن است مقدار null دریافت کنید
someItem.getId()
در این روش یک ستون داریم که مقدارش از نوع bigint یا integer است و به ازای هر عمل درج مقدارش به ترتیب یک واحد زیاد می شود . پس مقدار تولید شده فقط در سطح همان جدول منحصر بفرد است .
مثال :
@Entity(name = "IdentitySample")
public class IdentitySample {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
تذکر : وقتی از این استراتژی استفاده کنید Hibernate عمل درج دسته ای ( batching) را غیر فعال می کند. جدای از غیر فعال شدن JDBC batching این روش با مدل ارث بری Table -per-class هم نمی تواند کار کند زیرا اون می تواند چندین subclass داشته باشد که دارای یک id باشند
روش Sequence :
برعکس روش قبلی که مقدار تولید شده برای id از نظر منحصربفردی فقط محدود به یک جدول می شد ( به عنوان مثال اگر نام جدول شما student باشد و از روش Identity برای تولید جدول استفاده کرده باشید در این صورت اگر مقداری برای id تولید بشود برابر ۴۳ باشد در این صورت این عدد فقط در داخل جدول student منحصربفرد است نه جداول دیگر ) . در روش Sequence یک جا و مکان خاصی برای تولید مقادیر منحصر بفرد وجود دارد و نام آن جا و مکان sequence object می باشد . این روش مستقل از جدول است و مقدار تولید شده توسط این روش در کل دیتابیس منحصربفرد است . این روش معایب روش قبلی را ندارد یعنی امکان بدست اوردن مقدار id تولید شده حتی قبل از عمل درج واقعی وجود دارد . ( بخاطر استفاده از الگوریتم Hilo که بعدا توضیحش خواهیم داد . )
عمل درج دسته ای ( JDBC batching ) را در Hibernate غیر فعال نمی کند . همچنین مدل ارث بری در Hibernate را محدود نمی کند .
@Entity(name = "sequenceSample")
public class sequenceSample {
@Id
@GenericGenerator(
name = "sequence" ,
strategy = "sequence"
parameters = {
@org.hibernate.annotations.Parameter(
name = "sequence",
value = "sequenc"
)
})
@GeneratedValue(generator = "sequence")
private Long id;
}
این روش مستقل از DB است و توسط خود Hibernate مدیریت می شود .
روش Table :
یک روش جایگزین مستقل از DB دیگری برای تولید id این روش جدول می باشد به طوری از یک یا چند جدول استفاده می شود تا مقدار id بعدی را در این جدول ها نگهداری کند . درحالی که دو روش قبلی فارغ از تراکنش هستند (transaction-less) استفاده از جدول در DB مارا مجبور می کند که اصول ACID را رعایت کنیم . برای مدیریت هم روندی چندین درخواست تولید id . این روش بخاطر هزینه های مدیریت هم روندی بالایی که دارد توصیه نمی شود .
@Entity(name = "tableSample")
public class TableSequenceSample {
@Id
@GenericGenerator(
name = "table",
strategy = "enhanced-table"
parameters = {
@org.hibernate.annotations.Parameter(
name = "table_name",
value = "sequence_table"
)
})
@GeneratedValue(generator = "table", strategy=GenerationType.TABLE)
private Long id;
}
الگوریتم Hilo :
همانطور که پیشتر گفتیم سیاست هایبرنت این است که عمل درج entity های تازه ایجاد شده تا جایی که می تواند به تاخیر بیاندازد و از طرف دیگر خواسته ما برنامه نویسان این است بلافاصله بعد از فراخوانی متد persist بتونیم مقدار id را برای آن entity در دست داشته باشیم . برای تامین این خواسته از یک الگوریتمی بنام hilo استفاده می شود این الگوریتم نسخه ها مختلفی دارد و در کل فلسفه ان به این شرح است :
به کمک دو مقدار عمل تولید اعداد متوالی منحصربفرد صورت می گیرد و در جایی نگهداری می شودهرموقع appی درخواست id برای entity خود کرد بهش منتسب کند حتی قبل از اینکه واقعا اون entity در DB درج شده باشد .
مراحل الگوریتم :
۱- پایه اعداد متوالی را در متغیر high value ذخیره کن
۲- اندازه این توالی از اعداد را در متغیر low value ذخیره کن
۳- مقدار متغیر high value را بردار و یک واحد بهش اضافه کن
۴- حال low value را ضربدر high value کن تا کران پایین بدست اید :
Lower bound= low value * high value
۵- طبق فرمول زیر کران بالا را محاسبه کن :
Upper bounds=(high value * low value) + low value -1
حالا اعداد داخل این دو کران را می توان به هر کسی که درخواست مقدار برای id اش می کند تخصیص داد .
در مقاله بعدی در مورد استراتژی های تولید id در خود Hibernate صحبت خواهیم کرد .
مطالب مورد بررسی دراین مقاله :مفهوم Fine-grained domain modelروشهای شناسایی entity ها و value type ها در یک domain model
بیشتر بخوانیدوقتی برای اولین بار شروع به درک و مدلسازی یک سیستم میکنید اولین مستنداتی که در حین اینکار تولید میشود پایههای اولیه مدل سازی بیزنسی (business model ) را تشکیل میدهد . خیلی ساده، میخواهیم بدانیم این سیستم بایستی چه نیازهایی از مشتری را پاسخ دهد و چگونه / چه موقع این نیازها را پاسخ دهد . درک سیستم همیشه کار ساده و سرراستی نیست فرض کنید شما قصد مدلسازی سیستم شبیه سازی کنترل ترافیک خطوط هوایی آمریکا را برعهده دارید یا سیستم پرداخت یک بانک را . شما یک برنامه نویس یک طراح نرمافزار و در یک کلام شما یک متخصص حوزه IT هستید نه مهندس هواپیما نه مهندس مکانیک نه حسابدار بانک و نه ….
بیشتر بخوانید