۳۰ شهریور ۱۴۰۳

Techboy

اخبار و اطلاعات روز تکنولوژی

ماندگاری جاوا با JPA و Hibernate: نهادها و روابط

با استفاده از یک نمونه برنامه کاربردی مبتنی بر جاوا 21 با Hibernate 6.3، مدل سازی موجودیت ها و روابط برای ماندگاری داده جاوا را شروع کنید.

با استفاده از یک نمونه برنامه کاربردی مبتنی بر جاوا ۲۱ با Hibernate 6.3، مدل سازی موجودیت ها و روابط برای ماندگاری داده جاوا را شروع کنید.

Jakarta Persistence API (JPA) یک مشخصات جاوا است که شکاف بین پایگاه داده های رابطه ای و برنامه نویسی شی گرا را پر می کند. این آموزش دو قسمتی JPA را معرفی می‌کند و توضیح می‌دهد که چگونه اشیاء جاوا به عنوان موجودیت‌های JPA مدل‌سازی می‌شوند، چگونه روابط موجودیت تعریف می‌شوند، و چگونه از EntityManager JPA با الگوی Repository در برنامه‌های جاوا خود استفاده کنید. این همه اصول اولیه برای ذخیره و بارگیری وضعیت برنامه را در اختیار شما قرار می دهد.

توجه داشته باشید که این آموزش از Hibernate به عنوان ارائه‌دهنده JPA استفاده می‌کند. بیشتر مفاهیم را می توان به سایر چارچوب های ماندگاری جاوا تعمیم داد.

درباره نویسندگان

این آموزش در اصل توسط استیون هاینز نوشته شده و در JavaWorld منتشر شده است. این توسط متیو تایسون برای آخرین نسخه‌های جاوا و Hibernate تا زمان نگارش به‌روزرسانی شده است.

روابط شی در JPA

پایگاه داده های رابطه ای به عنوان وسیله ای برای ذخیره داده های برنامه از دهه ۱۹۷۰ وجود داشته اند. در حالی که امروزه توسعه دهندگان جایگزین های زیادی برای پایگاه داده رابطه ای دارند، هنوز به طور گسترده در توسعه نرم افزار در مقیاس کوچک و بزرگ استفاده می شود.

اشیاء جاوا در زمینه پایگاه داده رابطه ای به عنوان موجودات تعریف می شوند. موجودیت ها اشیایی هستند که در جداولی قرار می گیرند که در آن ستون ها و سطرها را اشغال می کنند و در نتیجه وجودشان در برنامه بیشتر می شود. برنامه نویسان از کلیدهای خارجی و جداول پیوستن برای تعریف روابط بین موجودیت ها استفاده می کنند – یعنی روابط یک به یک، یک به چند، و چند به چند. ما از SQL (زبان جستجوی ساختاریافته) برای بازیابی و تعامل با داده ها در جداول جداگانه و در چندین جدول استفاده می کنیم.

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

JPA چیست؟

به JPA ​​چیست مراجعه کنید؟ مقدمه ای بر ماندگاری جاوا برای یادگیری در مورد تکامل JPA و چارچوب های مرتبط، از جمله EJB 3.0. و JDBC.

عدم تطابق امپدانس روابط شیء

شاید شما با اصطلاح عدم تطابق امپدانس روابط شی آشنا باشید، که به این چالش نگاشت اشیاء داده به یک پایگاه داده رابطه ای اشاره دارد. این عدم تطابق به این دلیل رخ می دهد که طراحی شی گرا به روابط یک به یک، یک به چند و چند به چند محدود نمی شود. در عوض، در طراحی شی گرا، ما به اشیا، ویژگی ها و رفتار آنها و نحوه ارتباط اشیا فکر می کنیم. دو مثال عبارتند از کپسولاسیون و ارث:

  • اگر یک شی حاوی شی دیگری باشد، آن را از طریق encapsulation – یک رابطه has-a تعریف می کنیم.
  • اگر شیئی تخصص شیء دیگری باشد، آن را از طریق ارث بری – یک رابطه is-a تعریف می کنیم.
Eclipse سازمانی جاوا بخار جمع می کند، MicroProfile می لغزد

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

به عبارت دیگر، نماد نقطه ساده در جاوا: myObject.anotherObject.aProperty مستلزم مقدار زیادی کار برای یک ذخیره‌سازی داده‌های رابطه‌ای است.

ORM: نگاشت شیء-رابطه ای

عدم تطابق بین طراحی شی‌گرا و مدل‌سازی پایگاه‌داده رابطه‌ای منجر به ایجاد کلاسی از ابزارها شده است که به‌طور خاص برای نقشه‌برداری شی رابطه‌ای (ORM) توسعه یافته‌اند. ابزارهای ORM مانند Hibernate، EclipseLink، OpenJPA و MyBatis مدل های پایگاه داده رابطه ای، از جمله موجودیت ها و روابط آنها را به مدل های شی گرا ترجمه می کنند. بسیاری از این ابزارها قبل از مشخصات JPA وجود داشتند، اما بدون استاندارد، ویژگی‌های آنها وابسته به فروشنده بود.

برای اولین بار به عنوان بخشی از EJB 3.0 در سال ۲۰۰۶ منتشر شد، Java Persistence API (JPA) به بنیاد Eclipse منتقل شد و در سال ۲۰۱۹ به Jakarta Persistence API تغییر نام داد. یک راه استاندارد برای حاشیه نویسی اشیاء ارائه می دهد تا بتوان آنها را نگاشت و در یک پایگاه داده رابطه ای ذخیره کرد. این مشخصات همچنین یک ساختار مشترک برای تعامل با پایگاه‌های داده را تعریف می‌کند. داشتن یک استاندارد ORM برای جاوا یکپارچگی را به پیاده سازی فروشنده می آورد، در حالی که انعطاف پذیری و افزودنی ها را نیز امکان پذیر می کند. به عنوان مثال، در حالی که مشخصات اولیه JPA برای پایگاه‌های داده رابطه‌ای قابل اجرا است، برخی از پیاده‌سازی‌های فروشنده JPA را برای استفاده با پایگاه‌های اطلاعاتی NoSQL گسترش داده‌اند.

شروع به کار با JPA

Java Persistence API یک مشخصات است، نه یک پیاده سازی: یک انتزاع رایج را تعریف می کند که می توانید در کد خود برای تعامل با محصولات ORM استفاده کنید. این بخش برخی از بخش های مهم مشخصات JPA را بررسی می کند.

شما یاد خواهید گرفت که چگونه:

  • مواد، فیلدها و کلیدهای اصلی را در پایگاه داده تعریف کنید.
  • روابط بین موجودات در پایگاه داده ایجاد کنید.
  • با EntityManager و روش‌های آن کار کنید.

تعریف موجودیت ها

برای تعریف یک موجودیت، باید کلاسی ایجاد کنید که با حاشیه نویسی @Entity حاشیه نویسی شده باشد. حاشیه نویسی @Entity یک حاشیه نویسی نشانگر است که برای کشف موجودیت های پایدار استفاده می شود. به عنوان مثال، اگر می خواهید یک موجودیت کتاب ایجاد کنید، آن را به صورت زیر حاشیه نویسی کنید:


@Entity
public class Book {
   ...
}

به‌طور پیش‌فرض، این موجودیت همانطور که با نام کلاس مشخص می‌شود، به جدول Book نگاشت می‌شود. اگر می‌خواهید این موجودیت را به جدول دیگری (و به صورت اختیاری، طرحی خاص) نگاشت کنید، می‌توانید از حاشیه‌نویسی @table استفاده کنید. در اینجا نحوه نگاشت کلاس Book به جدول BOOKS آمده است:


@Entity
@Table(name="BOOKS")
public class Book {
   ...
}

اگر جدول BOOKS در طرح PUBLISHING بود، می‌توانید طرح را به حاشیه‌نویسی @Table اضافه کنید:


@Table(name="BOOKS", schema="PUBLISHING")

نگاشت فیلدها به ستون ها

با نگاشت موجودیت به جدول، وظیفه بعدی شما این است که فیلدهای آن را تعریف کنید. فیلدها به عنوان متغیرهای عضو در کلاس تعریف می شوند که نام هر فیلد به نام ستون در جدول نگاشت می شود. همانطور که در اینجا نشان داده شده است، می توانید با استفاده از حاشیه نویسی @Column، این نگاشت پیش فرض را لغو کنید:


@Entity
@Table(name="BOOKS")
public class Book {
   private String name;
   @Column(name="ISBN_NUMBER")
   private String isbn;
   ...
}

در این مثال، ما نگاشت پیش‌فرض را برای ویژگی name پذیرفته‌ایم اما یک نگاشت سفارشی را برای ویژگی isbn تعیین کرده‌ایم. ویژگی name به ستون “name” نگاشت می شود، اما ویژگی isbn به ستون ISBN_NUMBER نگاشت می شود.

چرا پایتون با تحلیلگران کسب و کار جذب می شود؟

حاشیه نویسی @Column به ما امکان می دهد ویژگی های اضافی فیلد یا ستون را تعریف کنیم، از جمله طول، تهی بودن آن، اینکه آیا باید منحصر به فرد باشد، دقت و مقیاس آن (اگر مقدار اعشاری باشد) )، اینکه آیا قابل درج و به روز رسانی است، و غیره.

مشخص کردن کلید اصلی

یکی از الزامات جدول پایگاه داده رابطه ای این است که باید دارای یک کلید اصلی یا کلیدی باشد که به طور منحصر به فرد یک ردیف خاص در پایگاه داده را شناسایی کند. در JPA، از حاشیه نویسی @Id برای تعیین فیلدی به عنوان کلید اصلی جدول استفاده می کنیم. کلید اصلی باید از نوع ابتدایی جاوا، یک پوشش اولیه مانند Integer یا Long، یک String، یک Date باشد. code>، یک BigInteger یا یک BigDecimal.

در این مثال، ویژگی id را که یک Integer است به ستون ID در جدول BOOKS نگاشت می‌کنیم:


@Entity
@Table(name="BOOKS")
public class Book {
   @Id
   private Integer id;
   private String name;
   @Column(name="ISBN_NUMBER")
   private String isbn;
   ...
}

همچنین می‌توان حاشیه‌نویسی @Id را با حاشیه‌نویسی @Column ترکیب کرد تا نگاشت نام ستون کلید اصلی را بازنویسی کرد.

روابط موجود در JPA

اکنون که می دانید چگونه یک موجودیت را تعریف کنید، بیایید نحوه ایجاد روابط بین موجودیت ها را بررسی کنیم. JPA چهار حاشیه نویسی را برای تعریف روابط بین موجودیت ها تعریف می کند:

  • @OneToOne
  • @OneToMany
  • @ManyToOne
  • @ManyToMany

روابط یک به یک

حاشیه نویسی @OneToOne برای تعریف رابطه یک به یک بین دو موجودیت استفاده می شود. به عنوان مثال، ممکن است یک موجودیت User داشته باشید که حاوی نام، ایمیل و رمز عبور کاربر است، اما ممکن است بخواهید اطلاعات اضافی درباره یک کاربر (مانند سن، جنسیت، و رنگ مورد علاقه) را در آن نگهداری کنید. یک موجودیت UserProfile جداگانه. حاشیه نویسی @OneToOne تجزیه داده ها و موجودیت های شما را به این روش تسهیل می کند.

کلاس User زیر دارای یک نمونه UserProfile است. UserProfile به یک نمونه User نگاشت می شود.


@Entity
public class User {
   @Id
   private Integer id;
   private String email;
   private String name;
   private String password;
   @OneToOne(mappedBy="user")
   private UserProfile profile;
   ...
}

@Entity
public class UserProfile {
   @Id
   private Integer id;
   private int age;
   private String gender;
   private String favoriteColor;
   @OneToOne
   private User user;
   ...
}

ارائه‌دهنده JPA از فیلد کاربری UserProfile برای نگاشت UserProfile به User استفاده می‌کند. نقشه برداری در ویژگی mappedBy در حاشیه نویسی @OneToOne مشخص شده است.

روابط یک به چند و چند به یک

حاشیه نویسی @OneToMany و @ManyToOne جنبه های مختلف یک رابطه را تسهیل می کند. مثالی را در نظر بگیرید که در آن یک کتاب می تواند فقط یک نویسنده داشته باشد، اما یک نویسنده ممکن است کتاب های زیادی داشته باشد. موجودیت Book یک رابطه @ManyToOne با Author و موجودیت Author یک @ تعریف می‌کند. رابطه OneToMany با Book:


@Entity
public class Book {
    @Id
    private Integer id;
    private String name;
    @ManyToOne
    @JoinColumn(name="AUTHOR_ID")
    private Author author;
    ...
}

@Entity
public class Author {
    @Id
    @GeneratedValue
    private Integer id;
    private String name;
    @OneToMany(mappedBy = "author")
    private List<Book> books = new ArrayList<>();
    ...
}

در این مورد، کلاس Author فهرستی از تمام کتاب‌های نوشته شده توسط آن نویسنده را نگه می‌دارد و کلاس Book به نویسنده‌ی مجرد خود اشاره می‌کند. علاوه بر این، @JoinColumn نام ستون را در جدول Book برای ذخیره شناسه Author مشخص می‌کند.

روابط چند به چند

در نهایت، حاشیه نویسی @ManyToMany یک رابطه چند به چند بین موجودیت ها را تسهیل می کند. در اینجا یک مورد وجود دارد که در آن موجودیت Book دارای چندین نویسنده است:


@Entity
public class Book {
    @Id
    private Integer id;
    private String name;
    @ManyToMany
    @JoinTable(name="BOOK_AUTHORS",
    		   joinColumns=@JoinColumn(name="BOOK_ID"),
    		   inverseJoinColumns=@JoinColumn(name="AUTHOR_ID"))
    private Set<Author> authors = new HashSet<>();
    ...
}

@Entity
public class Author {
    @Id
    @GeneratedValue
    private Integer id;
    private String name;
    @ManyToMany(mappedBy = "author")
    private Set<Book> books = new HashSet<>();
    ...
}

در این مثال، ما یک جدول جدید، BOOK_AUTHORS، با دو ستون ایجاد می کنیم: BOOK_ID و AUTHOR_ID. استفاده از ویژگی‌های joinColumns و inverseJoinColumns به چارچوب JPA شما می‌گوید که چگونه این کلاس‌ها را در یک رابطه چند به چند نگاشت کنید. حاشیه نویسی @ManyToMany در کلاس Author به فیلدی در کلاس Book اشاره می کند که رابطه را مدیریت می کند، یعنی نویسندگان دارایی.

کار با EntityManager

EntityManager کلاسی است که تعاملات پایگاه داده را در JPA انجام می دهد. از طریق یک فایل پیکربندی به نام persistence.xml یا با استفاده از حاشیه نویسی مقداردهی اولیه می شود. هر رویکرد مزایایی دارد. حاشیه نویسی پیکربندی را نزدیک به کلاس پیکربندی شده نگه می دارد، که ساده تر است، در حالی که فایل XML پیکربندی را خارج از کد نگه می دارد و در برنامه های مختلف قابل اشتراک گذاری است.

در این مثال از persistence.xml استفاده خواهیم کرد. فایل در پوشه META-INF در CLASSPATH شما، که معمولاً در فایل JAR یا WAR شما بسته بندی می شود، یافت می شود. فایل persistence.xml حاوی موارد زیر است: 

  • نام "واحد پایداری"، که چارچوب ماندگاری مورد استفاده شما را مشخص می‌کند، مانند Hibernate یا EclipseLink.
  • مجموعه ای از ویژگی ها که نحوه اتصال به پایگاه داده شما و همچنین هرگونه سفارشی سازی در چارچوب ماندگاری را مشخص می کند.
  • لیستی از کلاس های موجود در پروژه شما.

بیایید به یک مثال نگاه کنیم.

پیکربندی EntityManager

ابتدا، یک EntityManager ایجاد می کنیم. چند راه برای انجام این کار وجود دارد، از جمله استفاده از EntityManagerFactory که از کلاس Persistence بازیابی شده است. در بسیاری از سناریوها، این کلاس با یک ظرف IoC مانند Spring یا با Java CDI (Contexts and Dependency Injection) تزریق می شود. برای سادگی در برنامه مستقل خود، بیایید EntityManager را در یک مکان تعریف کنیم، و سپس از طریق EntityManagerFactory به آن دسترسی پیدا کنیم، مانند:


EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("Books");
EntityManager entityManager = entityManagerFactory.createEntityManager();

در این مورد، ما یک EntityManager ایجاد کرده‌ایم که به واحد ماندگاری "Books" متصل است. به زودی EntityManager را در عمل خواهید دید.

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