مروری اجمالی به آنچه که هایبرنیت می‌کند - قسمت اول

در این مقاله قصد دارم از ارتفاع ۱۰هزارپایی به کلیت هایبرنیت نگاهی بیاندازم تا شاید بتوان حدودی مشخص برای آن تعیین کرد. گرچه بمنظور عملی بودن مقاله، در خیلی مواقع فرودی اضطراری خواهیم داشت. اگر بتوانم به این هدف دست یابم، احتمالا کار خود و دیگر دوستانی که بدنبال مباحث Hibernate و بطور کلی ORMها هستند را راحت‌تر کرده‌ام.

فهرست مطالب

  • فلسفه‌ی هایبرنیت
  • راه‌اندازی هایبرنیت


فلسفه‌ی هایبرنیت

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

هایبرنیت یک فریمورک برای ذخیره‌سازی و بازیابیِ آبجکت‌های جاوا درون پایگاه‌داده است. تکرار می‌کنم، هایبرنیت ابزاری است که Java Objectها را درون پایگاه‌داده Persist / Save می‌کند و طبعا در زمان مورد نیاز Retrieve می‌کند.

تاکید من بر Java Objectها از این منظر است که تفاوتی جدی بین JDBC و فریمورک هایبرنیت قائل شویم. گرچه خود این فریمورک در نهایت از درایور JDBC برای ارتباط با پایگاه‌داده استفاده می‌کند، اما این امکان را به ما می‌دهد که با رابطی متفاوت، رویکرد شی‌گرای خودمان را حفظ کنیم.

اگر هیچ اطلاعی از پیش راجع به هایبرنیت نداشته‌اید، و مطالب بالا را گنگ و بی‌معنی می‌بینید، تنها یک توصیه به شما دارم: باید تحمل و صبر بیشتری بخرج دهید. من فقط می‌توانم قول دهم که به محض دست به کد شدن تمام گرفتاری‌های ذهنی راجع به فلسفه‌ی هایبرنیت برایتان برطرف خواهد شد، و کافی است بعد از آن یکبار دیگر این مقدمه‌ی کوتاه را مطالعه کنید تا بنظرتان شفاف و گویا بیاید. همراه باشید!

وقتی صحبت از هایبرنیت می‌کنم،‌ منظورم دقیقا به فریمورک ORM آن است. احتمالا شما نیز چنین برداشتی را کرده بودید که صحبت از چیست. اما در سایت هایبرنیت تاکید کرده است که هایبرنیت بیشتر از یک فریمورک ORM (یا همان Object Relational Mapping) است. فلذا مجددا تاکید می‌کنم که هدف این مقاله شرح Hibernate ORM می‌باشد.

هایبرنیت فریمورک‌‌های دیگری نظیر Validator ، Search ، OGM و... را دارد. فریمورک ORM مخصوص عملیات نگاشت بین آبجکت‌های جاوا و پایگاه‌داده‌ی رابطه‌ای است. بعبارت دیگر هایبرنیت مابین پایگاه‌داده‌ی رابطه‌ای و آبجکت‌های جاوا قرار می‌گیرد و آن‌ها را بهم تبدیل می‌کند.

پرسش. آیا هایبرنیت فقط برای پایگاه‌داده‌های رابطه‌ای نظیر MySQL, SQL Server و Oracle است؟ پس برای پایگاه‌داده‌های غیر رابطه‌ای نظیر MongoDB نمی‌توان از هایبرنیت استفاده کرد؟

پاسخ. اگر منظور شما از هایبرنیت، Hibernate ORM است، باید به پرسش اول بگویم بله، Hibernate ORM فقط برای پایگاه‌داده‌های رابطه‌ای است نظیر همان‌هایی که گفتید و ده‌ها سیستم پایگاه‌داده‌ایِ رابطه‌ای دیگر! و به پرسش دوم باید بگویم خیر، نمیتوان از آن برای MongoDB استفاده کرد. ولی می‌توان از Hibernate OGM که برای نگاشت گراف‌ها طراحی شده است برای این منظور استفاده کرد.
پرسش. آیا Hibernate OGM در این مقاله شرح داده‌ خواهد شد؟

جواب. خیر، فقط فریمورک ORM را بررسی خواهیم کرد. به همین خاطر در هر کجای مقاله زمانی که صحبت از هایبرنیت است دقیقا منظورم Hibernate ORM می‌باشد. و به همین ترتیب، هرجا صحبت از پایگاه‌داده می‌کنم منظورم پایگاه‌داده‌های رابطه‌ای است.

همانطور که عرض کردم، هایبرنیت مابین پایگاه‌داده و آبجکت‌های جاوا ایستاده است و عملیات Mapping را انجام می‌دهد. از آنجایی که دیگر ما نگران این نیستیم که چطور قرار است در پایگاه‌داده ذخیره‌سازی انجام دهیم، کاملا با رویکرد شی‌گرای خودمان کار را پیش می‌بریم.

بدون هایبرنیت برنامه‌نویس خودش مجبور است که نگاشت بین آبجکت‌ها و موجودیت‌های سمت پایگاه‌داده را انجام دهد. و اینکار جدا از اینکه پیچیدگی‌های خود را می‌داشت، از نظر مفهومی ممکن بود اهرم فشاری باشد تا برنامه‌نویس در طول کار در بعضی موارد رویکرد شی‌گرا را از دست بدهد.


https://www.javaguides.net/2018/12/what-is-difference-between-hibernate-and-spring-data-jpa.html
https://www.javaguides.net/2018/12/what-is-difference-between-hibernate-and-spring-data-jpa.html
پرسش. چرا نیاز به Mapping است؟

پاسخ. ما باید اطلاعات برنامه‌ی خودمان را در پایگاه‌داده ذخیره کنیم. در سمت کد‌های جاوا، ما با مفاهیمی بنام property، کلاس و آبجکت طرف هستیم و در سمت پایگاه‌داده با مفاهیمی نظیر سطر، ستون، صفت، جدول و تاپل (tuple) طرف هستیم. به عبارت دیگر ما باید یک کلاس را به یک جدول تبدیل کنیم، و هر آبجکت را به تاپلی (سطر) از جدول نگاشت کنیم.
جدا از این‌ها، ما در سمت جاوا، مفاهیم دیگری نظیر وراثت و ارتباط Composition را داریم، در سمت پایگاه‌داده مفاهیم ارتباط بین موجودیت‌ها و چندی رابطه (Cardinality) را داریم. برگردیم به سوال، چه نیازی به نگاشت است؟ ما باید اطلاعاتی که در سمت جاوا بدست می‌آید را درون پایگاه‌داده ذخیره کنیم و اطلاعاتی را که درون پایگاه‌داده ذخیره شده است را بتوانیم به کد‌های جاوا بیاوریم. چطور این‌ها را باید بهم نگاشت کرد؟ هایبرنیت این کار را برای ما انجام می‌دهد.

از قابلیت‌های خوب دیگر که هایبرنیت به ما می‌دهد (بغیر از ORM که توضیح داده شد)، این است که میزان کدهای نوشته شده توسط برنامه‌نویس نسبت به jdbc به شدت کاهش پیدا می‌کند. اما از آن مهمتر اینکه هایبرنیت برنامه‌نویسی را کاملا از مدل دیتابیس مستقل می‌کند. این امکان فوق‌العاده به شما اجازه میده که بدون اندیشیدن به آن که قرار است با چه پایگاه‌داده‌ای کار کنید (Mysql باشد یا Postgresql؟ فرقی نمیکند) کد بزنید و نگران نوع نگاشت آن نباشید (بر عهده‌ی هایبرنیت است). بعبارتی شما می‌توانید بدون هیچ هزینه‌ای و فقط با تغییری کوچک در یک فایل تنظیمات، دیتابیس خود را بطور مثال از Oracle به MySQL تغییر دهید.

و اما آخرین نکته‌ای که باید در قسمت فلسفه‌ی هایبرنیت گفت؛ در اصل هایبرنیت یک پیاده‌سازی (implementation) از یک استاندارد دیگر است. بعبارتی JPA (یا همان Java Persistence API) یک استاندارد است که چگونگی نگاشت و تمام آنچه که مربوط به مبحث ORM می‌شود را در خودش مشخص کرده است، و هایبرنیت یک فریمورک است که این استاندارد را اجرا کرده است. فلذا ما هنگام استفاده از هایبرنیت ناگزیر هستیم JPA را نیز تاحدودی بشناسیم، و از آن استفاده کنیم. البته کاملا مشخص است که این دو، جدا از هم نیستند. JPA استاندارد ( یا به عبارتی interface) و هایبرنیت پیاده‌سازی (implementation) است.

اطلاعات بیشتر راجع به JPA را در اینترنت جست‌وجو کنید. هرآنچه که این مقاله به آن نیاز داشته باشد را در خلل مثال‌ها توضیح خواهد داد، و دیگر توضیحی مستقل راجع به JPA نخواهیم داشت.

می‌توان برای اطلاعات بیشتر به فصل ۴، فصل ۵ و فصل ۶ کتاب Beginning Java EE 7 نوشته‌ی آقای Goncalves مراجع کنید.


راه‌اندازی هایبرنیت

خب، تا اینجای کار توضیح دادم که هایبرنیت چیست و قرار است برای ما چکاری انجام دهد. حال باید راجع به چگونگی آن صحبت کنم.

می‌خواهم خیلی زود و با کم‌ترین مقدمه دست‌بکار شویم، و خودمان را کمی درگیر کدنویسی کنیم. سپس اطلاعات بیشتر را بعد از آن ارائه دهم.


فرایند راه‌اندازی هایبرنیت بصورت گام به گام!

  • گام۰. آماده‌سازی محیط
  • گام۱. اضافه کردن کتابخانه‌های موردنیاز به پروژه.
  • گام۲. اضافه کردن فایل تنظیماتِ هایبرنیت به پروژه.
  • گام۳. آماده کردن کلاس‌های جاوا برای نگاشت به دیتابیس.
  • گام۴. اجرای عملیاتهای پایگاه‌داده‌ای.


گام۰. آماده‌سازی محیط

گام صفر را ساخت پروژه و آماده‌سازی دیتابیس درنظر بگیرید. برای من اهمیت ندارد که شما در چه محیطی کد می‌نویسید، یا روی چه پروژه‌ای کار می‌کنید. آیا وب‌اپلیکیشن می‌نویسید؟ یا یک برنامه‌ی کنسول؟ این‌ها در نوع استفاده از هایبرنیت اهمیت ندارد. گرچه بطور مثال اگر از Spring Boot استفاده می‌کنید می‌توانید از Starter های مروبط به JPA استفاده کنید و عملا گام ۱ را حذف کنید. ولی در هر حال پیشنهاد می‌کنم نوع کانفیگ را بیاموزید، تا بتوانید در هر نوع پروژه‌ای آن را بکار بگیرید. چنانچه از عهده‌ی این کار برنیامدید پس از خواندن این بخش (راه‌اندازی هایبرنیت) مشخصا نوع نیاز خودتان را سرچ کنید. بعنوان مثال هایبرنیت با Spring Boot.

من برای مدیریت پروژه از Maven استفاده می‌کنم، پیشنهاد می‌کنم شما هم چنین کنید، یا از Gradle استفاده کنید. استفاده از Maven جدا از مزیت‌هایش، پروژه را مستقل از ide نیز می‌کند (بعبارتی IDE Agnostic). اگر نمی‌دانید Maven چیست در یوتیوب سرچ کنید و آن را یادبگیرید.

پس پروژه‌ی خود را هر طور که صلاح می‌دانید بسازید. حال به سراغ پایگاه‌داده برویم.

خب، از چه پایگاه‌داده‌ای استفاده کنیم؟ این بار هم هر طور صلاح می‌دانید تصمیم بگیرید. اگر از Oracle استفاده می‌کردید، الان هم می‌توانید استفاده کنید. تنها الزام این بخش آن است پایگاه‌داده باید رابطه‌ای باشد. تکرار می‌کنم هایبرنیت تقریبا تمام RDBMSها را پشتیبانی می‌کند. چنانچه از دیتابیس متداولی استفاده نمی‌کنید و نگران هستید که آیا هایبرنیت از آن پشتیبانی می‌کند یا خیر، می‌توانید در این باره نیز سرچ کنید. من از MySQL استفاده می‌کنم. حال باید یک پایگاه‌داده درون MySQL بمنظور استفاده در این پروژه ایجاد کنم. اینکه چطور این کار را می‌کنید هم باز بخودتان مربوط است، از محیط‌های گرافیکی استفاده می‌کنید یا دستوری، فرقی ندارد. عملا این بخش اصلا ارتباطی با هایبرنیت ندارد از لحاظ مفهومی. من دستور زیر را در کنسول MySQL می‌نویسم:

CREATE DATABASE hibernate;

خب تقریبا مطمئن هستم که تمام RDBMSها این دستور را پشتیبانی می‌کنند. بعد از آن که مطمئن شدم پایگاه‌داده به درستی ایجاد شده است به سراغ گام بعدی می‌رویم. برای مطمئن شدن نیز می‌توانید دستور زیر را بزنید تا لیستی از تمام دیتابیس‌ها داشته باشید. همراه باشید!

SHOW DATABASES;


گام۱. اضافه کردن کتابخانه‌های موردنیاز به پروژه

طبعا نیاز است تا JAR فایل‌ها یا همان کتابخانه‌های هایبرنیت را به پروژه اضافه کنیم. برای اینکار می‌توان به صفحه‌ی هایبرنیت رفت و آن را دانلود کرد. توجه کنید که بهتر است نسخه‌ی stable را دانلود کنید. در زمان نگارش این مقاله نسخه‌ی ۵ پایدار است و نسخه‌ی ۶ در حال توسعه می‌باشد.

زمانی که فایل zip را دانلود کردید، به پوشه‌ی lib روید. در آن پوشه یک پوشه‌ی دیگر بنام required وجود دارد. تمام آن JAR فایل‌هایی که ما برای پروژه‌ی خود نیاز داریم در آن وجود دارد. بعبارتی می‌توان از تمام آنچه دانلود کردیم فقط این پوشه را به class path خود اضافه کنیم.

از چه محیطی برای کد زدن استفاده می‌کنید؟ eclipse یا IntelliJ IDEA؟ یا چه ابزاری برای مدیریت وابستگی‌ها؟ maven؟ gradle؟ هیچ‌کدام؟ قطعا بیاد دارید که گفتم این‌ها به این مقاله مرتبط نیست، بسته به نوع سلیقه و ذائقه‌ی خودتان رفتار کنید. من از IntelliJ و Maven استفاده می‌کند. بر اساس نوع سلیقه‌ی خودتان کتابخانه‌ای که به آن اشاره کردیم را به پروژه‌ی خودتان اضافه کنید. من از Maven استفاده می‌کنم درنتیجه به فایل POM در پروژه وابستگی زیر را اضافه می‌کنم:

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.2.17.Final</version>
</dependency>

خب یک چیز دیگر باقی مانده است. قطعا از بخش فلسفه‌ی هایبرنیت بخاطر دارید که اشاره کردم هایبرنیت در نهایت و دور از چشم ما، از JDBC استفاده می‌کند. فحوای کلامم این است که باید درایور مربوط به دیتابیس را هم اضافه کنیم (در این جا MySQL).

ما نیاز به JAR فایلِ MySQL Connector داریم. آن را به هر شکل که می‌دانید به پروژه‌ی خودتان اضافه کنید. من هم باید فایل POM را با وابستگی زیر بروز کنم:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.11</version>
</dependency>

خب بنظر دیگر کار تمام شد. ساده‌تر از آن چیزی بود که شاید فکرش را می‌کردید. اجازه دهید خلاصه کنم تا برای همیشه بیادتان بماند و سپس به سراغ گام بعدی برویم. در این بخش ۲ چیز نیاز داشتیم:

  1. کتاب‌خانه‌های مربوط به هایبرنیت
  2. کتاب‌خانه‌ی مربوط به اتصال jdbc به پایگاه‌داده


گام۲. اضافه کردن فایل تنظیماتِ هایبرنیت به پروژه

و اما شروع کار با خود هایبرنیت. هایبرنیت برای این که بداند به کدام دیتابیس متصل شود و چه رفتاری داشته باشد، به یک فایل کانفیگ استناد می‌کند.

ساختار پروژه تاکنون
ساختار پروژه تاکنون

ساختار پروژه‌ی من تاکنون چنین است. یک فایل xml بنام hibernate.cfg.xml در دایرکتوری resources ایجاد می‌کنم. این فایل hibernate.cfg.xml دقیقا همان فایل کانفیگی است که هایبرنیت به آن استناد می‌کند. شما اگر از maven استفاده نمی‌کنید در همان دایرکتوری src این فایل را بسازید.

پرسش. آیا حتما نام این فایل باید hibernate.cfg.xml باشد؟ یا جایش حتما باید مشخص باشد؟
پاسخ. خیر. میتوان نام دلخواهی برای آن گذاشت، یا در هر جایی از پروژه قرار داد. نام و مکان پیش‌فرض به ما کمک می‌کند تا بدون گفتن مکان و نام فایل، فریمورک هایبرنیت آن را پیدا کند. در غیراین صورت باید بطور شفاف به هایبرنیت بگوییم که فایل تنظیمات نامش چیست و در کجا قرار گرفته. در پروژه‌های معمولی مسیر پیش‌فرض src می‌باشد و نام hibernate.cfg.xml و در پروژه‌های maven نیز همچنین، با این تفاوت که مسیر پیش‌فرض دایرکتوری resources می‌باشد.

و اما محتویات این فایل. همانطور که گفتم یک فایل تنظیمات xml است، که dtd مشخص دارد. من فایل را برای شما می‌گذارم و سپس اجزای آن را شرح می‌دهم.


<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
        
        
<!--محتویات فایل hibernate.cfg.xml-->


<hibernate-configuration>
    <session-factory>

        <!-- JDBC Connection Settings -->
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://localhost:3306/hibernate?useSSL=false</property>
        <property name="connection.username">newuser</property>
        <property name="connection.password">password</property>

        <!-- Select SQL Dialect-->
        <property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
        

        <!-- Hibernate Settings-->
        <property name="hbm2ddl.auto">create</property>
        <property name="connection.pool_size">20</property>
        <property name="hibernate.current_session_context_class">thread</property>

        <property name="show_sql">true</property>


    </session-factory>
</hibernate-configuration>

من دیگر اشاره‌ای به جزئیات نمی‌کنم که بطور مثال تنظیمات درون تگ hibernate-configuration قرار می‌گیرد و بعد از آن تنظیمات مورد نظر ما درون تگ session-factory است. یا نام propertyها را بیان کنم. لطفا تیزبینانه نگاه کنید و این فایل را کپی نکنید، بنویسید تا بهتر متوجه اتفاقات شوید.

بخش اول، مربوط به تنظیمات اتصال به دیتابیس است. کلاس Driver و url و همچنین نام کاربری و پسورد را وارد می‌کنیم. توجه کنید که اطلاعات درایور و url برای اتصال به MySQL است، و اطلاعات نام‌کاربری و پسورد مربوط به سیستم پایگاهی من است (کپی نکنید!). قطعا متوجه شدید که اطلاعاتی را وارد می‌کنید در این بخش که همیشه هنگام کار با jdbc هم آن‌ها را استفاده می‌کردید.

محتویات فایل تنظیمات بهمراه ساختار پروژه تاکنون
محتویات فایل تنظیمات بهمراه ساختار پروژه تاکنون

بخش دوم، Dialect هایبرنیت است. اگر بخواهم خودمانی بگویم که اینجا چه چیز را تنظیم می‌کنیم اینطور می‌شود که به هایبرنیت می‌گوییم با چه لهجه‌ای با دیتابیس SQL ما صحبت کن. درست است که SQL استانداردی مشخص است، اما پیاده‌سازی آن در بین RDBMSها با کمی تفاوت سینتکسی همراه است. در این بخش این ابهامات را برطرف می‌کنیم (با مشخص کردن اینکه با لهجه‌ی MySQL صحبت کن).

پرسش. من از اوراکل استفاده می‌کنم، چطور Dialect مربوط به آن را پیدا کنم؟
پاسخ. جست‌وجو در اینترنت! لیست آنها در اینترنت هست.

بخش‌های بعدی هم در رابطه با این است که هایبرنیت چطور رفتار کند. اجازه دهید از توضیح بیشتر بپرهیزیم، و به گام بعدی برویم. و بعد از آن وارد جزییات بیشتری می‌شویم.


گام۳. آماده کردن کلاس‌های جاوا برای نگاشت به دیتابیس

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

اجازه دهید اینطور تصور کنیم که می‌خواهیم اطلاعات دانشجویان را در دیتابیس ذخیره کنیم. ما در دیتابیس جدول دانشجو خواهیم داشت که ستون‌های آن باید id، sname، stell و saddress باشد که به ترتیب شماره‌ی دانشجو، نام دانشجو، تلفن دانشجو و آدرس دانشجو هستند. در جاوا نیز قطعا کلاسی خواهیم داشت که این اطلاعات را میگیرد.

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

پس در اینجا، من شروع می‌کنم کلاس Student را می‌سازم. اما این کلاس باید ویژگی‌های خاصی داشته باشد. دقیق‌تر، این کلاس باید یک POJO ساده باشد. برای نوشتن یک Plain Old Java Object چه چیزهایی الزامی است؟

  1. تمام propertyها متدهای getter و setter داشته باشند.
  2. کلاس یک سازنده‌ی بدون آرگومان (no-args constructor) داشته باشد.
پرسش. آیا باید این constructor را ساخت؟ تا جاییکه من میدانم خود کامپایلر چنین سازنده‌ای را ایجاد می‌کند.
پاسخ. اگر هیچ سازنده‌ی دیگری در کلاس وجود نداشته باشد، کامپایلر چنین سازنده‌ی پیش‌فرضی را ایجاد می‌کند. اما چنان‌چه شما یک (یا چند) سازنده‌ی دیگر با آرگومان لحاظ کرده باشید، باید صراحتا خودتان یک سازنده‌ی بدون آرگومان ایجاد کنید.

کلاس داشنجو باید مشابه زیر شده باشد:

package entities;

public class Student {
    
    private int id;
    private String name;
    private String tell;
    private String address;

    
    public Student() {} // non-arg constructor
    
    // Setters & Getters
    
}

من مشخصه‌های تلفن و آدرس را نیز از نوع String در نظر گرفتم (برای سهولت کار). و کاملا مشخص است که از آوردن متدهای setter & getter پرهیز شده است. شما برای خودتان باید بنویسید. کمی بعد عکسی از این کلاس خواهم گذاشت تا ابهامات احتمالی برطرف شود.

خب، سوالی اساسی اینجا مطرح می‌شود. چطور باید به هایبرنیت بفهمانم که این کلاس باید به جدولی بنام student در دیتابیس تبدیل شود، و چطور اسم مشخصه‌ی name را sname بگذارد و چطور به دیتابیس بگوید که فیلد id را کلیداصلی (primary key) در نظر بگیرد؟!

باید راهی باشد تا نقشه‌ی نگاشت را به هایبرنیت گفت، تا به کمک آن نقشه هایبرنیت عملیات نگاشت را انجام دهد.

پیش‌تر ها چنین نقشه‌ای را در یک فایل دیگر xml قرار می‌دادند و هایبرنیت به کمک آن فایل میفهمید که با کلاس Student چطور باید رفتار کند. اینکه آیا اصلا این کلاس را باید به جدولی تبدیل کند؟ اگر بله، به چه نامی؟ اینکه چطور با فیلدهای آن رفتار کند، کلید اصلی کدام فیلد است، اسم فیلدها در دیتابیس چگونه باشد، و چه محدودیت‌هایی برای آن بگذارد و... را در آن فایل xml به هایبرنیت معرفی می‌کردیم.

امروزه چکار می‌کنند؟ امروزه عمدتا از راه آسان‌تری استفاده می‌شود. از Annotationها استفاده می‌کنیم. با استفاده از Annotationها می‌توانیم تمام آن اطلاعات را خیلی آسان‌تر و شفاف‌تر به هایبرنیت بدهیم. پس وقت آن است که شروع به معرفی Annotationهای نگاشت کنیم.

پرسش. Annotation چی هست؟
پاسخ. Annotation همانطور که از ترجمه‌ی لغوی آن پیداست یکجور حاشیه نویسی است. می‌توان آن را به چشم متادیتا دید (اطلاعاتی اضافه). فریمورکی همچون هایبرنیت از این اطلاعات اضافه برای چگونگی نگاشت استفاده می‌کنند. استفاده از Annotationها بسیار رایج است، بخصوص در استفاده از فریمورک‌هایی نظیر Spring یا هایبرنیت. حتما در Java Core هم از Annotationهایی نظیر @Override استفاده کرده‌اید.

اولین Annotationی که باید استفاده کنیم تا به هایبرنیت بگوید این کلاس باید تبدیل به یک موجودیت در دیتابیس شود، @Entity است. این Annotation به هایبرنیت می‌گوید که این کلاس را تبدیل به یک جدول کن (به محاوره‌ترین شکل ممکن گفتم!).

تصویر زیر حالت کامل شده است، مشاهده کنید و به تمام Annotationهای آن توجه کنید.

کلاس Student
کلاس Student


خیلی خلاصه ادامه می‌دهم و بعد از گام۴ به آن برخواهم گشت و جز به جز توضیح خواهم داد.

در خط ۶ به کمک @Table و شناسه‌ی name به هایبرنیت گفتم که می‌خواهم چه نامی برای این کلاس در دیتابیس درنظر بگیرد. @Table اختیاری است، چنانچه استفاده نکنید هایبرنیت نام کلاس را بعنوان اسم جدول قرار می‌دهد (که در این مثال هیچ فرقی ندارد!).

در خط ۹ به هایبرنیت گفتم که فیلد id کلید اصلی من در جدول می‌باشد (با کمک @Id) و با @GeneratedValue هم وظیفه‌ی یکتا بودن کلید را به هایبرنیت سپردم. به این معنا که دیگر خود هایبرنیت وظیفه‌ی تولید id را به گردن دارد. (مشابه Auto Increment در دیتابیس).

در خط‌های بعد از @Column استفاده کردم. استفاده از این Annotation اختیاری است. به این معنا که در نبودن آن هایبرنیت اطلاعات پیش‌فرض را قرار می‌دهد. بعنوان مثال در مورد فیلد address هیچ Annotationی لحاظ نشده است، به این خاطر هایبرنیت نام ستون را دقیقا با نام فیلد قرار می‌دهد. اما در مورد فیلد name به هایبرنیت گفته شده است که نام این ستون را sname قرار بده.

نکته‌ی بسیار مهم در رابطه با این Annotationها این است که همه‌ی آنها از پکیج javax.persistence می‌باشد. بعبارتی این‌ها Annotationهای JPA هستند و نه Hibernate. البته هایبرنیت هم مشابه آنها را دارد، اما خود هایبرنیت توصیه می‌کند که از Annotationهای پکیج JPA استفاده بشود.

شاید برایتان جالب باشد که هایبرنیت قدمت بیشتری از JPA دارد، بعبارتی ابتدا هایبرنیت متولد شد، با بخت و اقبال مواجه شد، به همین خاطر استانداردی (JPA) برای آن در نظر گرفتند.

پرسش. من همچنان متوجه @Id نشدم. این Annotation صرفا کلیداصلی را مشخص می‌کند؟
پاسخ. بله، همین است. چیز پیچیده‌ای نیست.
پرسش. چطور بفهمم @GeneratedValue چه چیز تولید می‌کند؟ میتوانم بر آن مدیریت داشته باشم؟
پاسخ. بله، میتوان مدیریت کرد. ولی بطور پیشفرض چند استراتژی از پیش تعیین شده هم برای استفاده گذاشته‌اند. پاسخ دقیق به این سوال را در این قسمت صلاح نمی‌دانم، فقط بدانید که بله می‌شود مدیریت کرد.
پرسش. آیا می‌توانم فیلدی را کلیداصلی در نظر بگیرم ولی @GeneratedValue را استفاده نکنم؟
پاسخ. بله می‌شود. آنگاه خودتان باید یکتایی مقدار را تضمین کنید.
پرسش. اگر بخواهم چند فیلد را باهم بعنوان کلید نظر بگیرم، باید برای هرکدام از آن فیلد از @Id استفاده کنم؟
پاسخ. خیر، ابدا. این موضوع را در قسمت دوم بخش کلیداصلی مرکب توضیح خواهم داد، تا آن زمان صبور باشید.

خب اجازه دهید تمام کد را هم به اشتراک بگذارم و به سراغ گام بعد برویم.

package entities;

import javax.persistence.*;

@Entity
@Table(name = "student")
public class Student {

    @Id @GeneratedValue
    private int id;
    @Column(name = "sname")
    private String name;
    @Column(name = "stell")
    private String tell;
    private String address;


    public Student() {} // non-arg constructor

    public Student(String name, String tell, String address) {
        this.name = name;
        this.tell = tell;
        this.address = address;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        return id == student.id;
    }

    @Override
    public int hashCode() {
        return id;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", tell='" + tell + '\'' +
                ", address='" + address + '\'' +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getTell() {
        return tell;
    }

    public void setTell(String tell) {
        this.tell = tell;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}
پرسش. چرا متدهای hashCode و equals هم ایجاد شده است؟
پاسخ. این متدها به هایبرنیت در انجام تضمین یگانگی فیلد id کمک می‌کند. همانطور که ملاحظه می‌کنید متد equals فقط برای فیلد id ایجاد شده است. البته این کار الزامی نیست. یکجور سفت کاری است!
پرسش. اجازه دهید! من دارم گیج می‌شوم. حجم مطالب زیاد شده است و شما پرحرفی زیادی میکنید. باعث شده است من گم کنم!
پاسخ. حق دارید، ولی نگران نباشید. تنها یک گام دیگر تا معجزه باقی مانده است. کار زیادی تا الان انجام ندادیم، فقط مجبور به پرحرفی بودیم که موجب نگرانی شما شده است.
تاکنون ما کتابخانه‌های موردنیاز را اضافه کردیم، یک فایل کانفیگ ساختیم و اطلاعات ارتباط با دیتابیس را وارد آن کردیم، و در نهایت یک کلاس جاوای خود را به کمک Annotationها به Entity تبدیل کردیم. و حالا هم میرویم که تست انجام دهیم. به همین سادگی. همراه باشید!


گام۴. اجرای عملیاتهای پایگاه‌داده‌ای

تست کردن و گام آخر
تست کردن و گام آخر


هرآنچه که برای آزمودن هایبرنیت می‌بایست انجام دهیم در تصویر بالا مشخص است. در این تصویر من ابتدا یک شی از کلاس Student ایجاد کرده‌ام و سپس آن را درون دیتابیس ذخیره (save) کرده‌ام. خروجی را میخواهید در دیتابیس مشاهده کنید؟ عکس زیر را ملاحظه کنید.

خروجی جدول دانشجو
خروجی جدول دانشجو

دقیقا همان است که انتظار داشتیم. دوستان من خسته نباشید. نفسی تازه کنید، اکنون دریافتید که هایبرنیت چطور کار می‌کند. اما هیچ توضیحی راجع به این کلاس‌ها و آبجکت‌های عجیب و غریب که در فایل Test است ندادیم. محتویات کلاس Test را مشاهده کنید:

package entities;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class Test {

    public static void main(String[] args) {

        SessionFactory sessionFactory = new Configuration().configure()
                .addAnnotatedClass(Student.class)
                .buildSessionFactory();

        Session session = sessionFactory.getCurrentSession();

        Student student = new Student("Mohamad", "09121112233", "Tehran");

        session.beginTransaction();

        session.save(student);

        session.getTransaction().commit();

        session.close();
        sessionFactory.close();
    }
}

کلاس SessionFactory فایل تنظیمات را می‌خواند (به کمک کلاس Configuration) و یک آبجکت سنگین (به این خاطر که تنها یکبار ساخته می‌شود) ایجاد می‌کند. در خط ۱۴ من به تنظیمات این را اضافه کردم که به کلاس Student نگاه کند. این کار باید صورت بگیرد، در غیر این صورت هایبرنیت این کلاس را برای نگاشت درنظر نمی‌گیرد.

کلاس Session ارتباط jdbc را Wrap کرده است. کلاس اصلی برای ذخیره و بازیابی اطلاعات از پایگاه داده است. شی این کلاس از کلاس SessionFactory گرفته می‌شود.

سپس یک شی از کلاس Student ایجاد کردیم (بنام student)، و در نهایت تراکنشی را به کمک متد beginTransaction از کلاس Session شروع کردیم و پس از آن به کمک متد save از کلاس Session مقدار student را درون دیتابیس ذخیره کردیم. و در آخر تراکنش را commit کردیم.

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

لطفا نظر خودتون رو کامنت کنید، آیا توانستید از این مطلب استفاده کنید؟

حمایت معنوی از گروه داده‌کاوی دایکه