آچار ابری با Hibernate ORM

1. بررسی اجمالی

Hibernate تبدیل به راه حل استاندارد ORM برای پروژه های جاوا شده است. این از همه پایگاه داده های رابطه ای اصلی پشتیبانی می کند و ابزارهای ORM قدرتمندتری مانند Spring Data JPA را فعال می کند. علاوه بر این، بسیاری از فریمورک‌های سازگار با Hibernate مانند Spring Boot، Microprofile و Quarkus وجود دارد.

Cloud Spanner Dialect برای Hibernate ORM امکان استفاده از Hibernate با Cloud Spanner را فراهم می کند. با تداوم اصطلاحی Hibernate، از مزایای Cloud Spanner - مقیاس‌پذیری و معناشناسی رابطه‌ای برخوردار می‌شوید. این می‌تواند به شما کمک کند برنامه‌های موجود را به ابر منتقل کنید یا برنامه‌های جدیدی بنویسید که بهره‌وری توسعه‌دهنده افزایش یافته توسط فناوری‌های مبتنی بر Hibernate فراهم می‌شود.

چیزی که یاد خواهید گرفت

  • چگونه یک برنامه Hibernate ساده بنویسیم که به Cloud Spanner متصل می شود
  • نحوه ایجاد پایگاه داده Cloud Spanner
  • نحوه استفاده از گویش Cloud Spanner برای Hibernate ORM
  • نحوه اجرای عملیات ایجاد، خواندن، به‌روزرسانی، حذف (CRUD) با Hibernate

آنچه شما نیاز دارید

2. راه اندازی و الزامات

تنظیم محیط خود به خود

  1. به کنسول Cloud وارد شوید و یک پروژه جدید ایجاد کنید یا از یک موجود استفاده مجدد کنید. (اگر قبلاً یک حساب Gmail یا G Suite ندارید، باید یک حساب ایجاد کنید .)

k6ai2NRmxIjV5iMFlVgA_ZyAWE4fhRrkrZZ5mZuCas81YLgk0iwIyvgoDet4s2lMYGC5K3xLSOjIbmC9kjiezvQuxuhdYRolbv1rft1lOmZuCas81YLgk0iwIyvgoDet4s2lMYGC5K3xLSOjIbmC9kjiezvQuxuxhdYRolbv1rft1lOmZuCas81YLgk0iwIyvgoDet4s2lMYGC5K3xLSOjIbmC9kjiezvQuxuhdYRolbv1rft1lOmZuCas81YLcaz

UtcCMcSYtCOrEWuILx3XBwb3GILPqXDd6cJiQQxmylg8GNftqlnE7u8aJLhlr1ZLRkpncKdj8ERnqcH71wab2HlfUnO9CgXKd0-CAQC2t3CHw0w0

KoK3nfWQ73s_x4QI69xqzqdDR4tUuNmrv4FC9Yq8vtK5IVm49h_8h6x9X281hAcJcOFDtX7g2BXPvP5O7SOR2V4UI6W8gN6cTJq8V0dWT-Jq8V0VT-HRW8V0DWT-JqSV000s QCXA6w

شناسه پروژه را به خاطر بسپارید، یک نام منحصر به فرد در تمام پروژه های Google Cloud (نام بالا قبلاً گرفته شده است و برای شما کار نخواهد کرد، متأسفیم!). بعداً در این آزمایشگاه کد به عنوان PROJECT_ID نامیده خواهد شد.

  1. در مرحله بعد، برای استفاده از منابع Google Cloud، باید صورت‌حساب را در Cloud Console فعال کنید .

اجرا کردن از طریق این کد لبه نباید هزینه زیادی داشته باشد، اگر اصلاً باشد. حتماً دستورالعمل‌های موجود در بخش «تمیز کردن» را دنبال کنید که به شما توصیه می‌کند چگونه منابع را خاموش کنید تا بیش از این آموزش متحمل صورت‌حساب نشوید. کاربران جدید Google Cloud واجد شرایط برنامه آزمایشی رایگان 300 دلاری هستند.

Cloud Shell را فعال کنید

  1. از Cloud Console، روی Activate Cloud Shell کلیک کنید R47NpBm6yyzso5vnxnRBikeDAXxl3LsM6tip3rJxnKuS2EZdCI0h-eIOGm9aECq8JXbMFlJkd68uTutXU8gGmQUVa5iI1OdZczXP2tzqpWRwMTZWARC QA .

STsYbcAtkIQyN6nL9BJhld3Fv5KxedYynpUVcRWwvIR-sYMMc4kfK-unIYgtsD4P6T0P8z-A12388tPmAh-Trsx80qobaW4KQXHJ7qJIXWHi6r ciw

اگر قبلاً Cloud Shell را راه‌اندازی نکرده‌اید، با یک صفحه میانی (زیر تاشو) روبرو می‌شوید که آن را توصیف می‌کند. اگر اینطور است، روی Continue کلیک کنید (و دیگر آن را نخواهید دید). در اینجا به نظر می رسد که آن صفحه یک بار مصرف:

LnGMTn1ObgwWFtWpjSdzlA9TDvSbcY76GiLQLc_f7RP1QBK1Tl4H6kLCHzsi89Lkc-serOpqNH-F2XKmV5AnBqTbPon4HvCwSSrY_ERFxlH5T

تهیه و اتصال به Cloud Shell فقط باید چند لحظه طول بکشد.

hfu9bVHmrWw01Hnrlf4MBNja6yvssDnZzN9oupcG12PU88Vvo30tTluX9IySwnu5_TG3U2UXAasX9eCwqwZtc6Yhwxri95zG82DLUcKxrF7TvV0ZaQnKxrFtv0JoXn

این ماشین مجازی با تمام ابزارهای توسعه که شما نیاز دارید بارگذاری شده است. این دایرکتوری اصلی 5 گیگابایتی دائمی را ارائه می دهد و در Google Cloud اجرا می شود و عملکرد شبکه و احراز هویت را بسیار افزایش می دهد. بیشتر، اگر نه همه، کار شما در این کد لبه را می توان به سادگی با یک مرورگر یا Chromebook انجام داد.

پس از اتصال به Cloud Shell، باید ببینید که قبلاً احراز هویت شده اید و پروژه قبلاً روی ID پروژه شما تنظیم شده است.

  1. برای تایید احراز هویت، دستور زیر را در 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 برای ایجاد یک برنامه کنسول جاوا ساده استفاده خواهیم کرد.

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 بررسی کنیم.

b5cb37d043d4d2b0.png

تا کنون، ما فقط یک برنامه پایه کنسول جاوا داریم که "Hello World!" . با این حال، ما واقعاً می خواهیم یک برنامه جاوا بنویسیم که از Hibernate برای صحبت با Cloud Spanner استفاده کند. برای آن، به گویش Cloud Spanner برای Hibernate، درایور Cloud Spanner JDBC و هسته 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>

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 و رشته اتصال 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

به یاد داشته باشید که ID پروژه خود را جایگزین {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. کلاس های موجودیت مشروح ایجاد کنید

حالا ما آماده ایم تا مقداری کد بنویسیم.

ما دو شیء قدیمی جاوا (POJOs) را تعریف می کنیم که به جداول در Cloud Spanner نگاشت می شوند - Singer و Album . Album یک رابطه @ManyToOne با Singer خواهد داشت. همچنین می‌توانستیم Singer را با حاشیه‌نویسی @OneToMany به فهرست Album نگاشت کنیم، اما برای این مثال، ما واقعاً نمی‌خواهیم همه آلبوم‌ها را هر بار که نیاز به واکشی خواننده از پایگاه داده داریم، بارگیری کنیم.

کلاس های 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() ذخیره می کنیم و برخی از پرس و جوها را با استفاده از زبان Query 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 را در پایگاه داده مشاهده کنید، چیزی شبیه به این خواهید دید:

f18276ea54cc266f.png

952d9450dd659e75.png

9. پاکسازی کنید

بیایید نمونه Cloud Spanner را که در ابتدا ایجاد کرده بودیم حذف کنیم تا مطمئن شویم که منابع را بی جهت مصرف نمی کند.

gcloud spanner instances delete codelab-instance

10. تبریک می گویم

تبریک می‌گوییم، شما با موفقیت یک برنامه جاوا ساخته‌اید که از Hibernate برای تداوم داده‌ها در Cloud Spanner استفاده می‌کند.

  • شما یک نمونه Cloud Spanner و یک پایگاه داده ایجاد کردید
  • شما برنامه را برای استفاده از Hibernate پیکربندی کرده اید
  • شما دو موجودیت ایجاد کردید: Artist و Album
  • شما به طور خودکار طرح پایگاه داده را برای برنامه خود ایجاد کردید
  • شما با موفقیت موجودیت ها را در Cloud Spanner ذخیره کردید و از آنها پرس و جو کردید

اکنون مراحل کلیدی مورد نیاز برای نوشتن یک برنامه Hibernate با Cloud Spanner را می دانید.

بعدش چی؟