Collection ها در Hibernate جاوا
از سری مقالات آموزش Java Enterprise, مبحث Collection ها در Hibernate را بر میگزینیم.
در مقاله قبل به مفهوم Embedded Object و Value Object ها درHibernate پرداختیم. در این مقاله به مفاهیم سطح بالاتری نسبت به مقاله قبل میپردازیم. در آموزش قبل تنها دو Instance از Location داشتیم. اما اگر چندین نشانی داشته باشیم, دیگر چنین روشی برای تعریف چندین نشانی منطقی نیست. در واقع وقتی با یک لیست سروکار داریم, تعداد اعضای لیست برای ما مشخص نیست. یک نرم افزار اجتماعی میتواند مثال مناسبی باشد. در یک نرم افزار اجتماعی, تعداد نظرات برای یک مطلب همیشه ثابت نیست. ممکن است برای یک مطلب هیچ نظری ثبت نشود و گاهی برای یک مطلب چندین نظر ثبت میشود. در نتیجه نمیتوان ابعداد مشخصی برای نظرات در نظر گرفت. در اینجاست که روش قبل دیگر جوابگو نیست و باید از روشی استفاده کنیم تا Collection ها را بتوانید در Hibernate ذخیره کنیم.
در روش قبل, تعداد column هایی که برای ذخیره سازی اطلاعات در پایگاه داده نیاز داشتیم, مشخص بود. اما در این روش مشخص نیست به چه تعدادی از column نیاز داریم. بنابراین بهتر است یک جدول دیگر با ساختار کلاس Location ایجاد شود. بنابراین object های کلاس Location مانند object های کلاس User در جدول مختص به خود ثبت میشوند. در نهایت نیاز است تا با استفاده از یک کلید, ارتباط میان این دو جدول را برقرار کنیم. در کلاس User, مقدار id به عنوان primary key است. این مقدار منحصر به object است و هیچ دو object, مقدار یکسان primary key را اختیار نمیکنند. id هر object در جدول مربوط به کلاس Location, و در سطر location آن object ذخیره میشود. برای مثال اگر object مقدار ۱ را برای id بپذیرد و دو نشانی داشته باشد, در جدول location این دو نشانی, در یک column مقدار ۱ را نگهداری میکنند. به این ترتیب اتصال این مقادیر با User Object حفظ میشود.
به مثال مقاله قبل برمیگردیم و متغیر visited location را به صورت یک لیست از Location ایجاد میکنیم.
private List<Location> visitedLocation;
همانطور که میدانیم, با استفده از دستور بالا یک لیست از کلاس Location برای ما ایجاد میشود. اما در Hibernate mapping, از کلاس Set برای این منظور استفاده میشود. مزیتی که کلاس Set دارد این است که دو عضو یکسان را در خود ذخیره نمیکند. برای مثال متغیر a و b در آن وجود ندارند که a.equal(b)=true باشد. همچنین مقادیر null را در خود جای نمیدهد. در نتیجه, بجای کد فوق, از این کد استفاده میکنیم:
private java.util.Set<Location> visitedLocation;
مانند روش قبل, در این روش نیز نیاز است تا با استفاده از یک annotation, مشخص کنیم که متغیر ما یک لیست است. element collection annotation این امکان را برای ما فراهم میکند:
@ElementCollection private java.util.Set<Location> visitedLocation;
برای ذخیره سازی اطلاعات چنین کدی را مینویسیم:
user.setVisitedLocation(new HashSet<>()); Location location = new Location(); location.setCity("...."); location.setCountry("....."); location.setLatitude(0); location.setLongitude(0); Location location2 = new Location(); location2.setCity("...."); location2.setCountry("....."); location2.setLatitude(1); location2.setLongitude(1); user.getVisitedLocation().add(location); user.getVisitedLocation().add(location2);
وقتی کد فوق را اجرا میکنیم, چنین دستورات SQL در خروجی نمایش داده میشود:
Hibernate: insert into User (home_city, home_county, home_latitute, home_longitude, name, city, country, latitude, longitude, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) Hibernate: insert into User_visitedLocation (User_id, city, country, latitude, longitude) values (?, ?, ?, ?, ?)
همانطور که مشاهده میکنید, یک جدول جدید برای این نوع از متغیر ها ایجاد کرده است و با استفاده از primary key جدول user, این دو جدول را به یکدیگر مرتبط کرده است. اما اگر بخواهیم نام این جدول را بطور دلخواه تنظیم کنیم, از Join table annotation استفاده میکنیم:
@ElementCollection @JoinTable(name = "user_visited_address") private java.util.Set<Location> visitedLocation;
با اجرای کد فوق, نام جدول دیگر user_visitedLocation ذخیره نمیشود و بجای آن از نام user_visited_location استفاده میکند. میتوان به join table annotation, یک join column annotation اضافه کرد و با استفاده از آن, نام column که نقطه اتصال میان دو جدول است را به دلخواه تنظیم کرد:
@ElementCollection @JoinTable(name = "user_visited_address", joinColumns = @JoinColumn(name="u_id")) private java.util.Set<Location> visitedLocation;
اگر بخواهیم id برای Location ها تعریف کنیم, نیاز است تا ساختار set را به Collection تغییر دهیم. چون index در HashSet وجود ندارد.
@ElementCollection @JoinTable(name = "user_location", joinColumns = @JoinColumn(name = "user_location_id")) private Collection<Location> visitedLocation = new ArrayList<>();
برای افزودن id به collection از collection id annotation استفاده میکنیم. این annotation سه مقدار column, Generator و Type را دریافت میکند. ابتدا بررسی میکنیم که Identifier Generator را بررسی میکنیم. Hibernate این امکان را دارد تا مقادیر Identifier ها را به صورت خودکار ایجاد کند. Identifier ها مقادیر تعیین کننده و تمایز دهنده در جداول هستند. برای مثال id در کلاس User یک Identifier است. Hibernate از چندین روش برای مقدار دهی خودکار این Identifier ها استفاده میکند:
- Identity : از Identity column ها در MySQL, DB2 و MS SQL Server پشتیبانی میکند. مقادیر آن از نوع short, long و یا int است.
- Sequence : از الگوریتم hi / lo استفاده میکند. روش کار این الگوریتم به این صورت است که عدد را به دو بخش پر ارزش و کم ارزش تقسیم میکند. client, وقتی میخواهد یک داده جدید وارد کند, مقدار پر ارزش را یک واحد افزایش میدهد. به این ترتیب این اطمینان حاصل میشود که در این بازه از اعداد, هیچ مقداری از client دیگری ثبت نمیشود و تداخلی رخ نمیدهد. برای مثال عدد فعلی ۱۰ است. Client, وقتی میخواهد مقداری را وارد کند, شمارنده را یک واحد افزایش میدهد و مقدار شمارنده ۱۱ میشود. اعداد کم ارزش, در بازه ۰ تا ۱۰۰ مقدار میپذیرند. به این ترتیب Client برای افزودن داده ها, عدد پر ارزش را برابر ۱۰ و عدد کم ارزش را برای هر داده تغییر میدهد. داده اول مقدار ۱۰/۱, داده دوم مقدار ۱۰/۲ و به ترتیب به همین صورت ثبت میشوند. در اینجا اگر Client دیگری همزمان داده ای را وارد کند, مقدار پر ارزش را یک واحد افزایش میدهد (مقدار پر ارزش ۱۲ میشود) و از ۱۱ برای مقدار پر ارزش و از ۰ تا ۱۰۰ برای مقدار کم ارزش استفاده میکند. با استفاده از این الگوریتم, دسته بندی اطلاعات نیز آسان تر می شود
- table : در این روش, یک یا چند table عدد sequence number را نگهداری میکنند. این روش از ACID استفاده میکند. اعداد باید طی یک پردازش مجزا پایگاه داده, محاسبه شوند و برای این منظور از Isolation Delegation استفاده می شود. Isolation Delegation مشخص میکند که این پردازش در تراکنشی مجزا انجام شود.
- auto : هریک از روش های فوق که همخوانی بهتری با پایگاه داده ای که استفاده میشود را داشته باشد, انتخاب میشود.
اکنون طبق تعاریف فوق, از collection id annotation استفاده میکنیم:
@GenericGenerator(name = "zeroToHeroGenerator",strategy="sequence") @CollectionId(columns = @Column(name = "visited_id"),type = @Type(type = "long"),generator = "zeroToHeroGenerator") private Collection<Location> visitedLocation = new ArrayList<>();
در کد بالا یک Generic Generator برای مقدار generator در Collection id تعریف و مشخص کردهایم که با چه ساختاری در پایگاه داده ذخیره شود.
در این مقاله سعی بر این شد تا بتوانیم با استفاده از مفاهیمی که تا کنون بررسی کردیم, لیست ها را در پایگاه داده ذخیره کنیم. یک لیست دارای اندازه های متفاوتی است و نمیتوان لیست را محدود به اندازه خاصی کرد. بنابراین در این مقاله ذخیره سازی Collection ها در Hibernate را بررسی کردیم.
سری مقالات آموزش Java Enterprise ادامه دارد.
با ما همراه باشید.
دیدگاهتان را بنویسید
برای نوشتن دیدگاه باید وارد بشوید.