1. نظرة عامة
أصبح Hibernate حلّ إدارة علاقات الكائنات (ORM) المعتمَد تلقائيًا لمشاريع Java. وهو يتيح استخدام جميع قواعد البيانات العلائقية الرئيسية، كما يتيح استخدام أدوات إدارة علاقات الكائنات (ORM) أكثر فعالية، مثل Spring Data JPA. بالإضافة إلى ذلك، هناك العديد من الأُطر المتوافقة مع Hibernate، مثل Spring Boot وMicroprofile وQuarkus.
يتيح Cloud Spanner Dialect for Hibernate ORM استخدام Hibernate مع Cloud Spanner. يمكنك الاستفادة من مزايا Cloud Spanner، مثل قابلية التوسّع والدلالات العلائقية، مع إمكانية الحفاظ على البيانات بطريقة Hibernate المألوفة. يمكن أن يساعدك ذلك في نقل التطبيقات الحالية إلى السحابة الإلكترونية أو كتابة تطبيقات جديدة تستفيد من زيادة إنتاجية المطوّرين التي توفّرها التقنيات المستندة إلى Hibernate.
المواضيع التي ستتعرّف عليها
- كيفية كتابة تطبيق Hibernate بسيط يتصل بـ Cloud Spanner
- كيفية إنشاء قاعدة بيانات Cloud Spanner
- كيفية استخدام Cloud Spanner Dialect for Hibernate ORM
- كيفية تنفيذ عمليات الإنشاء والقراءة والتعديل والحذف (CRUD) باستخدام Hibernate
المتطلبات
2. الإعداد والمتطلبات
إعداد بيئة التعلّم الذاتي
- سجِّل الدخول إلى Cloud Console وأنشِئ مشروعًا جديدًا أو أعِد استخدام مشروع حالي. (إذا لم يكن لديك حساب على Gmail أو G Suite، عليك إنشاء حساب.)
احفظ رقم تعريف المشروع، وهو اسم فريد لجميع مشاريع Google Cloud (الاسم أعلاه محجوز ولن يكون متاحًا لك، لذا يُرجى اختيار اسم آخر). سيُشار إليه لاحقًا في هذا الدرس التطبيقي حول الترميز باسم PROJECT_ID.
- بعد ذلك، عليك تفعيل الفوترة في Cloud Console لاستخدام موارد Google Cloud.
لن تتكبّد تكلفة كبيرة، إن وجدت، عند إكمال هذا الدرس التطبيقي حول الترميز. احرص على اتّباع أي تعليمات في قسم "التنظيف" الذي يقدّم لك إرشادات حول كيفية إيقاف الموارد حتى لا يتم تحصيل رسوم منك بعد هذا البرنامج التعليمي. يمكن للمستخدمين الجدد في Google Cloud الاستفادة من برنامج "الإصدار التجريبي المجاني" الذي يمنحهم رصيدًا بقيمة 300 دولار أمريكي.
تفعيل Cloud Shell
- من Cloud Console، انقر على تفعيل Cloud Shell
.
إذا لم يسبق لك بدء Cloud Shell، ستظهر لك شاشة وسيطة (الجزء السفلي غير المرئي من الصفحة) تصف هذه الخدمة. في هذه الحالة، انقر على متابعة (ولن تظهر لك هذه الشاشة مرة أخرى). في ما يلي شكل هذه الشاشة التي تظهر مرة واحدة:
لن يستغرق توفير Cloud Shell والاتصال به سوى بضع لحظات.
يتم تحميل هذه الآلة الافتراضية مزوّدة بكل أدوات التطوير التي ستحتاج إليها. وهي توفّر دليلًا رئيسيًا دائمًا بسعة 5 غيغابايت وتعمل على Google Cloud، ما يحسّن بشكل كبير أداء الشبكة والمصادقة. يمكنك إكمال معظم أعمالك في هذا الدرس التطبيقي حول الترميز، إن لم يكن كلّها، باستخدام متصفّح أو جهاز Chromebook فقط.
بعد الاتصال بـ Cloud Shell، من المفترض أن يظهر لك أنّه تمّت المصادقة عليك وأنّ المشروع تم ضبطه على رقم تعريف مشروعك.
- شغِّل الأمر التالي في Cloud Shell للتأكّد من أنّه تمّت المصادقة عليك:
gcloud auth list
ناتج الأمر
Credentialed Accounts
ACTIVE ACCOUNT
* <my_account>@<my_domain.com>
To set the active account, run:
$ gcloud config set account `ACCOUNT`
gcloud config list project
ناتج الأمر
[core] project = <PROJECT_ID>
إذا لم يكن كذلك، يمكنك تعيينه من خلال هذا الأمر:
gcloud config set project <PROJECT_ID>
ناتج الأمر
Updated property [core/project].
3. إنشاء قاعدة بيانات
بعد تشغيل Cloud Shell، يمكنك البدء باستخدام gcloud للتفاعل مع مشروعك على GCP.
أولاً، فعِّل Cloud Spanner API.
gcloud services enable spanner.googleapis.com
لننشئ الآن مثيل Cloud Spanner باسم codelab-instance.
gcloud spanner instances create codelab-instance \ --config=regional-us-central1 \ --description="Codelab Instance" --nodes=1
علينا الآن إضافة قاعدة بيانات إلى هذا المثيل. سنسمّيها codelab-db.
gcloud spanner databases create codelab-db --instance=codelab-instance
4. إنشاء تطبيق فارغ
سنستخدم Maven Quickstart Archetype لإنشاء تطبيق Java بسيط لوحدة التحكّم.
mvn archetype:generate \ -DgroupId=codelab \ -DartifactId=spanner-hibernate-codelab \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DarchetypeVersion=1.4 \ -DinteractiveMode=false
انتقِل إلى دليل التطبيق.
cd spanner-hibernate-codelab
جمِّع التطبيق وشغِّله باستخدام Maven.
mvn compile exec:java -Dexec.mainClass=codelab.App
من المفترض أن يظهر النص Hello World! في وحدة التحكّم.
5. إضافة التبعيات
لنستكشف رمز المصدر عن طريق فتح Cloud Shell Editor وتصفّح المحتوى داخل الدليل spanner-hibernate-codelab.

حتى الآن، لدينا تطبيق Java أساسي لوحدة التحكّم يطبع "Hello World!". ومع ذلك، نريد كتابة تطبيق Java يستخدم Hibernate للتواصل مع Cloud Spanner. لتحقيق ذلك، سنحتاج إلى Cloud Spanner Dialect for Hibernate وCloud Spanner JDBC driver وHibernate core. لنضِف إذن التبعيات التالية إلى الكتلة <dependencies> داخل الملف pom.xml.
pom.xml
<!-- Spanner Dialect -->
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-spanner-hibernate-dialect</artifactId>
<version>1.5.0</version>
</dependency>
<!-- JDBC Driver -->
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-spanner-jdbc</artifactId>
<version>2.0.0</version>
</dependency>
<!-- Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.29.Final</version>
</dependency>
6. ضبط Hibernate ORM
بعد ذلك، سننشئ ملفّي إعداد Hibernate، وهما hibernate.cfg.xml وhibernate.properties. شغِّل الأمر التالي لإنشاء الملفّين الفارغَين، ثم عدِّلهما باستخدام Cloud Shell Editor.
mkdir src/main/resources \ && touch src/main/resources/hibernate.cfg.xml \ && touch src/main/resources/hibernate.properties
لنخبر Hibernate عن فئات الكيانات التي تم وضع علامات عليها والتي سنربطها بقاعدة البيانات عن طريق ملء hibernate.cfg.xml. (سننشئ فئات الكيانات لاحقًا.)
src/main/resources/hibernate.cfg.xml
<hibernate-configuration>
<session-factory>
<!-- Annotated entity classes -->
<mapping class="codelab.Album"/>
<mapping class="codelab.Singer"/>
</session-factory>
</hibernate-configuration>
يحتاج Hibernate أيضًا إلى معرفة كيفية الاتصال بمثيل Cloud Spanner واللغة التي يجب استخدامها. لذا، سنطلب منه استخدام SpannerDialect لبنية SQL، وSpanner JDBC driver، وسلسلة اتصال JDBC مع إحداثيات قاعدة البيانات. يتم إدخال هذه المعلومات في الملف hibernate.properties.
src/main/resources/hibernate.properties
hibernate.dialect=com.google.cloud.spanner.hibernate.SpannerDialect
hibernate.connection.driver_class=com.google.cloud.spanner.jdbc.JdbcDriver
hibernate.connection.url=jdbc:cloudspanner:/projects/{PROJECT_ID}/instances/codelab-instance/databases/codelab-db
# auto-create or update DB schema
hibernate.hbm2ddl.auto=update
hibernate.show_sql=true
تذكَّر استبدال {PROJECT_ID} برقم تعريف مشروعك، الذي يمكنك الحصول عليه عن طريق تشغيل الأمر التالي:
gcloud config get-value project
بما أنّه ليس لدينا مخطط قاعدة بيانات حالي، أضفنا السمة hibernate.hbm2ddl.auto=update للسماح لـ Hibernate بإنشاء الجدولَين في Cloud Spanner عند تشغيل التطبيق للمرة الأولى.
عادةً، عليك أيضًا التأكّد من إعداد بيانات اعتماد المصادقة، إما باستخدام ملف JSON لحساب الخدمة في متغيّر البيئة GOOGLE_APPLICATION_CREDENTIALS أو بيانات الاعتماد التلقائية للتطبيق التي تم ضبطها باستخدام الأمر gcloud auth application-default login. ومع ذلك، بما أنّنا نشغِّل في Cloud Shell، يتم إعداد بيانات اعتماد المشروع التلقائية مسبقًا.
7. إنشاء فئات الكيانات التي تم وضع علامات عليها
نحن الآن جاهزون لكتابة بعض الرموز البرمجية.
سنحدّد كائنَين Java عاديَين (POJOs) سيتم ربطهما بالجدولَين في Cloud Spanner، وهما Singer وAlbum. سيكون لـ Album علاقة @ManyToOne مع Singer. كان بإمكاننا أيضًا ربط Singer بقوائم Album باستخدام العلامة @OneToMany، ولكن في هذا المثال، لا نريد تحميل جميع الألبومات في كل مرة نحتاج فيها إلى جلب مغنٍّ من قاعدة البيانات.
أضِف فئتَي الكيانَين Singer وAlbum.
أنشِئ ملفات الفئات.
touch src/main/java/codelab/Singer.java \ && touch src/main/java/codelab/Album.java
الصِق محتويات الملفات.
src/main/java/codelab/Singer.java
package codelab;
import java.util.Date;
import java.util.UUID;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.hibernate.annotations.Type;
@Entity
public class Singer {
@Id
@GeneratedValue
@Type(type = "uuid-char")
private UUID singerId;
private String firstName;
private String lastName;
@Temporal(TemporalType.DATE)
private Date birthDate;
public Singer() {
}
public Singer(String firstName, String lastName, Date birthDate) {
this.firstName = firstName;
this.lastName = lastName;
this.birthDate = birthDate;
}
public UUID getSingerId() {
return singerId;
}
public void setSingerId(UUID singerId) {
this.singerId = singerId;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Date getBirthDate() {
return birthDate;
}
public void setBirthDate(Date birthDate) {
this.birthDate = birthDate;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Singer)) {
return false;
}
Singer singer = (Singer) o;
if (!firstName.equals(singer.firstName)) {
return false;
}
if (!lastName.equals(singer.lastName)) {
return false;
}
return birthDate.equals(singer.birthDate);
}
@Override
public int hashCode() {
int result = firstName.hashCode();
result = 31 * result + lastName.hashCode();
result = 31 * result + birthDate.hashCode();
return result;
}
}
src/main/java/codelab/Album.java
package codelab;
import java.util.UUID;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import org.hibernate.annotations.Type;
@Entity
public class Album {
@Id
@GeneratedValue
@Type(type = "uuid-char")
UUID albumId;
@ManyToOne
Singer singer;
String albumTitle;
public Album() {
}
public Album(Singer singer, String albumTitle) {
this.singer = singer;
this.albumTitle = albumTitle;
}
public UUID getAlbumId() {
return albumId;
}
public void setAlbumId(UUID albumId) {
this.albumId = albumId;
}
public Singer getSinger() {
return singer;
}
public void setSinger(Singer singer) {
this.singer = singer;
}
public String getAlbumTitle() {
return albumTitle;
}
public void setAlbumTitle(String albumTitle) {
this.albumTitle = albumTitle;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Album)) {
return false;
}
Album album = (Album) o;
if (!singer.equals(album.singer)) {
return false;
}
return albumTitle.equals(album.albumTitle);
}
@Override
public int hashCode() {
int result = singer.hashCode();
result = 31 * result + albumTitle.hashCode();
return result;
}
}
يُرجى العِلم أنّه في هذا المثال، نستخدم رقم تعريف UUID تم إنشاؤه تلقائيًا للمفتاح الأساسي. هذا هو نوع رقم التعريف المفضّل في Cloud Spanner، لأنّه يتجنّب النقاط الساخنة عندما يقسّم النظام البيانات بين الخوادم حسب نطاقات المفاتيح. يمكن أيضًا استخدام مفتاح عدد صحيح متزايد بشكل رتيب، ولكن قد يكون أداؤه أقل.
8. حفظ الكيانات وطلبها
بعد ضبط كل شيء وتحديد كائنات الكيان، يمكننا البدء في الكتابة إلى قاعدة البيانات وطلبها. سنفتح Session في Hibernate، ثم نستخدمه أولاً لحذف جميع صفوف الجدول في الطريقة clearData()، وحفظ بعض الكيانات في الطريقة writeData()، وتشغيل بعض طلبات البحث باستخدام لغة طلبات البحث في Hibernate (HQL) في الطريقة readData().
استبدِل محتويات App.java بما يلي:
src/main/java/codelab/App.java
package codelab;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
public class App {
public final static DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
public static void main(String[] args) {
// create a Hibernate sessionFactory and session
StandardServiceRegistry registry = new StandardServiceRegistryBuilder().configure().build();
SessionFactory sessionFactory = new MetadataSources(registry).buildMetadata()
.buildSessionFactory();
Session session = sessionFactory.openSession();
clearData(session);
writeData(session);
readData(session);
// close Hibernate session and sessionFactory
session.close();
sessionFactory.close();
}
private static void clearData(Session session) {
session.beginTransaction();
session.createQuery("delete from Album where 1=1").executeUpdate();
session.createQuery("delete from Singer where 1=1").executeUpdate();
session.getTransaction().commit();
}
private static void writeData(Session session) {
session.beginTransaction();
Singer singerMelissa = new Singer("Melissa", "Garcia", makeDate("1981-03-19"));
Album albumGoGoGo = new Album(singerMelissa, "Go, Go, Go");
session.save(singerMelissa);
session.save(albumGoGoGo);
session.save(new Singer("Russell", "Morales", makeDate("1978-12-02")));
session.save(new Singer("Jacqueline", "Long", makeDate("1990-07-29")));
session.save(new Singer("Dylan"<;, &qu>ot;Shaw", makeDate("1998-05-02")));
sess>ion.getTransaction().commit();
}
private static void re<adDat>a(Session session) {
ListSinger singers = session.createQuery("from Singer where birthDate = '1990-01-01' order by lastName")
.list();
ListAlbum albums = session.createQuery("from Album").list();
System.out.println("Singers who were born in 1990 or later:");
for (Singer singer : singers) {
System.out.println(singer.getFirstName() + " " + singer.getLastName() + " born on "
+ DATE_FORMAT.format(singer.getBirthDate()));
}
System.out.println("Albums: ");
for (Album album : albums) {
System.out
.println("\"" + album.getAlbumTitle() + "\" by " + album.getSinger().getFirstName() + " "
+ album.getSinger().getLastName());
}
}
private static Date makeDate(String dateString) {
try {
return DATE_FORMAT.parse(dateString);
} catch (ParseException e) {
e.printStackTrace();
return null;
}
}
}
لنجمِّع الرمز البرمجي ونشغِّله الآن. سنضيف الخيار -Dexec.cleanupDaemonThreads=false لإيقاف التحذيرات بشأن تنظيف سلاسل المحادثات في الخلفية التي سيحاول Maven تنفيذها.
mvn compile exec:java -Dexec.mainClass=codelab.App -Dexec.cleanupDaemonThreads=false
من المفترض أن يظهر لك في الناتج ما يلي:
Singers who were born in 1990 or later: Jacqueline Long born on 1990-07-29 Dylan Shaw born on 1998-05-02 Albums: "Go, Go, Go" by Melissa Garcia
في هذه المرحلة، إذا انتقلت إلى Cloud Spanner Console وعرضت بيانات جدولَي Singer وAlbum في قاعدة البيانات، سيظهر لك ما يلي:


9. تَنظيم
لنحذف مثيل Cloud Spanner الذي أنشأناه في البداية للتأكّد من أنّه لا يستهلك الموارد بلا داعٍ.
gcloud spanner instances delete codelab-instance
10. تهانينا
تهانينا، لقد أنشأت بنجاح تطبيق Java يستخدم Hibernate للحفاظ على البيانات في Cloud Spanner.
- لقد أنشأت مثيل Cloud Spanner وقاعدة بيانات
- لقد ضبطت التطبيق لاستخدام Hibernate
- لقد أنشأت كيانَين، وهما Artist وAlbum
- لقد أنشأت تلقائيًا مخطط قاعدة البيانات لتطبيقك
- لقد حفظت الكيانات بنجاح في Cloud Spanner وطلبتها
أصبحت الآن على دراية بالخطوات الرئيسية المطلوبة لكتابة تطبيق Hibernate باستخدام Cloud Spanner.