۱. مرور کلی
Hibernate به راهکار استاندارد ORM برای پروژههای جاوا تبدیل شده است. این ابزار از تمام پایگاههای داده رابطهای اصلی پشتیبانی میکند و ابزارهای ORM قدرتمندتری مانند Spring Data JPA را نیز فعال میکند. علاوه بر این، چارچوبهای سازگار با Hibernate زیادی مانند Spring Boot، Microprofile و Quarkus وجود دارند.
زبان برنامهنویسی ابری اسپنر برای Hibernate ORM امکان استفاده از Hibernate را با Cloud Spanner فراهم میکند. شما از مزایای Cloud Spanner - مقیاسپذیری و معناشناسی رابطهای - به همراه پایداری اصطلاحی Hibernate بهرهمند میشوید. این میتواند به شما کمک کند تا برنامههای موجود را به ابر منتقل کنید یا برنامههای جدیدی بنویسید که از افزایش بهرهوری توسعهدهندگان ناشی از فناوریهای مبتنی بر Hibernate بهره میبرند.
آنچه یاد خواهید گرفت
- چگونه یک برنامه ساده Hibernate بنویسیم که به Cloud Spanner متصل شود؟
- نحوه ایجاد پایگاه داده Cloud Spanner
- نحوه استفاده از Cloud Spanner Dialect برای Hibernate ORM
- نحوه پیادهسازی عملیاتهای ایجاد-خواندن-بهروزرسانی-حذف (CRUD) با Hibernate
آنچه نیاز دارید
۲. تنظیمات و الزامات
تنظیم محیط خودتنظیم
- وارد Cloud Console شوید و یک پروژه جدید ایجاد کنید یا از یک پروژه موجود دوباره استفاده کنید. (اگر از قبل حساب Gmail یا G Suite ندارید، باید یکی ایجاد کنید .)
شناسه پروژه را به خاطر بسپارید، یک نام منحصر به فرد در تمام پروژههای Google Cloud (نام بالا قبلاً گرفته شده و برای شما کار نخواهد کرد، متاسفیم!). بعداً در این آزمایشگاه کد به آن PROJECT_ID گفته خواهد شد.
- در مرحله بعد، برای استفاده از منابع گوگل کلود، باید پرداخت را در Cloud Console فعال کنید .
اجرای این آزمایشگاه کد، اگر اصلاً هزینهای نداشته باشد، نباید هزینه زیادی داشته باشد. حتماً دستورالعملهای بخش «پاکسازی» را که به شما نحوه خاموش کردن منابع را آموزش میدهد، دنبال کنید تا پس از این آموزش، متحمل هزینه نشوید. کاربران جدید Google Cloud واجد شرایط برنامه آزمایشی رایگان ۳۰۰ دلاری هستند.
فعال کردن پوسته ابری
- از کنسول ابری، روی فعال کردن پوسته ابری کلیک کنید
.
اگر قبلاً Cloud Shell را شروع نکردهاید، یک صفحه میانی (در زیر صفحه) به شما نمایش داده میشود که توضیح میدهد چیست. در این صورت، روی ادامه کلیک کنید (و دیگر هرگز آن را نخواهید دید). آن صفحه یکبار مصرف به این شکل است:
آمادهسازی و اتصال به Cloud Shell فقط چند لحظه طول میکشد.
این ماشین مجازی با تمام ابزارهای توسعهای که نیاز دارید، مجهز شده است. این ماشین یک دایرکتوری خانگی ۵ گیگابایتی پایدار ارائه میدهد و در فضای ابری گوگل اجرا میشود که عملکرد شبکه و احراز هویت را تا حد زیادی بهبود میبخشد. بخش عمدهای از کار شما در این آزمایشگاه کد، اگر نگوییم همه، را میتوان به سادگی با یک مرورگر یا کرومبوک انجام داد.
پس از اتصال به 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].
۳. ایجاد یک پایگاه داده
پس از راهاندازی 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
۴. یک برنامه خالی ایجاد کنید
ما از Maven Quickstart Archetype برای ایجاد یک برنامه کنسول ساده جاوا استفاده خواهیم کرد.
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! را در کنسول چاپ شده ببینید.
۵. وابستگیها را اضافه کنید
بیایید با باز کردن ویرایشگر Cloud Shell و مرور درون دایرکتوری spanner-hibernate-codelab ، کد منبع را بررسی کنیم.

تا اینجا، ما فقط یک برنامه کنسول جاوا ساده داریم که عبارت "Hello World!" را چاپ میکند. با این حال، واقعاً میخواهیم یک برنامه جاوا بنویسیم که از Hibernate برای ارتباط با Cloud Spanner استفاده کند. برای این کار، به Cloud Spanner Dialect برای Hibernate، درایور JDBC Cloud Spanner و هسته Hibernate نیاز داریم. بنابراین، بیایید وابستگیهای زیر را به بلوک <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>
۶. پیکربندی Hibernate ORM
در مرحله بعد، فایلهای پیکربندی Hibernate hibernate.cfg.xml و hibernate.properties را ایجاد خواهیم کرد. دستور زیر را اجرا کنید تا فایلهای خالی ایجاد شوند و سپس آنها را با استفاده از ویرایشگر Cloud Shell ویرایش کنید.
mkdir src/main/resources \ && touch src/main/resources/hibernate.cfg.xml \ && touch src/main/resources/hibernate.properties
بنابراین، بیایید با پر کردن hibernate.cfg.xml ، به Hibernate در مورد کلاسهای موجودیت حاشیهنویسیشدهای که به پایگاه داده نگاشت خواهیم کرد، اطلاع دهیم. (کلاسهای موجودیت را بعداً ایجاد خواهیم کرد.)
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، درایور JDBC Spanner و رشته اتصال 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 اجرا میکنیم، اعتبارنامههای پیشفرض پروژه از قبل تنظیم شدهاند.
۷. ایجاد کلاسهای موجودیت حاشیهنویسیشده
حالا آمادهایم که کمی کد بنویسیم.
ما دو شیء ساده و قدیمی جاوا (POJO) تعریف خواهیم کرد که به جداول در Cloud Spanner نگاشت میشوند - Singer و Album . Album یک رابطه @ManyToOne با Singer خواهد داشت. ما همچنین میتوانستیم Singer ها را با حاشیهنویسی @OneToMany به لیست Album نگاشت کنیم، اما برای این مثال، واقعاً نمیخواهیم هر بار که نیاز به واکشی یک Singer از پایگاه داده داریم، همه آلبومها را بارگذاری کنیم.
کلاسهای موجودیت 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 ترجیح داده میشود، زیرا از نقاط حساس جلوگیری میکند، زیرا سیستم دادهها را بر اساس محدوده کلیدها بین سرورها تقسیم میکند. یک کلید عدد صحیح که به صورت یکنواخت افزایش مییابد نیز کار میکند، اما میتواند عملکرد کمتری داشته باشد.
۸. ذخیره و پرسوجو از موجودیتها
با پیکربندی همه چیز و تعریف اشیاء موجودیت، میتوانیم شروع به نوشتن در پایگاه داده و پرسوجو از آن کنیم. یک 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", "Shaw", makeDate("1998-05-02")));
session.getTransaction().commit();
}
private static void readData(Session session) {
List<Singer> singers = session.createQuery("from Singer where birthDate >= '1990-01-01' order by lastName")
.list();
List<Album> 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 را اضافه خواهیم کرد تا هشدارهای مربوط به پاکسازی رشتههای daemon که 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 بروید و دادههای جداول Singer و Album را در پایگاه داده مشاهده کنید، چیزی شبیه به این خواهید دید:


۹. تمیز کردن
بیایید نمونه Cloud Spanner را که در ابتدا ایجاد کردیم حذف کنیم تا مطمئن شویم که منابع را بیجهت مصرف نمیکند.
gcloud spanner instances delete codelab-instance
۱۰. تبریک
تبریک میگویم، شما با موفقیت یک برنامه جاوا ساختید که از Hibernate برای ذخیره دادهها در Cloud Spanner استفاده میکند.
- شما یک نمونه Cloud Spanner و یک پایگاه داده ایجاد کردید
- شما برنامه را برای استفاده از Hibernate پیکربندی کردهاید
- شما دو موجودیت ایجاد کردید: هنرمند و آلبوم
- شما به طور خودکار طرحواره پایگاه داده را برای برنامه خود ایجاد کردید
- شما با موفقیت موجودیتها را در Cloud Spanner ذخیره کردید و از آنها کوئری گرفتید
اکنون مراحل کلیدی مورد نیاز برای نوشتن یک برنامه Hibernate با Cloud Spanner را میدانید.