Cloud Spanner dengan Hibernate ORM

Hibernate telah menjadi solusi ORM standar de facto untuk proyek Java. Ini mendukung semua database relasional utama dan memungkinkan alat ORM yang lebih kuat seperti Spring Data JPA . Selain itu, ada banyak kerangka kerja yang kompatibel dengan Hibernate seperti Spring Boot, Microprofile, dan Quarkus.

Cloud Spanner Dialect untuk Hibernate ORM memungkinkan penggunaan Hibernate dengan Cloud Spanner. Anda mendapatkan manfaat Cloud Spanner - skalabilitas dan semantik relasional - dengan ketekunan idiomatik dari Hibernate. Ini dapat membantu Anda memigrasi aplikasi yang ada ke cloud atau menulis yang baru dengan memanfaatkan peningkatan produktivitas pengembang yang diberikan oleh teknologi berbasis Hibernate.

Apa yang akan Anda pelajari

  • Cara menulis aplikasi Hibernate sederhana yang terhubung ke Cloud Spanner
  • Cara membuat database Cloud Spanner
  • Cara menggunakan Cloud Spanner Dialect untuk Hibernate ORM
  • Cara mengimplementasikan operasi create-read-update-delete (CRUD) dengan Hibernate

Apa yang Anda butuhkan

  • Proyek Google Cloud Platform
  • Browser, seperti Chrome atau Firefox

Pengaturan lingkungan mandiri

  1. Masuk ke Cloud Console dan buat proyek baru atau gunakan kembali proyek yang sudah ada. (Jika Anda belum memiliki akun Gmail atau G Suite, Anda harus membuatnya .)

k6ai2NRmxIjV5iMFlVgA_ZyAWE4fhRrkrZZ5mZuCas81YLgk0iwIyvgoDet4s2lMYGC5K3xLSOjIbmC9kjiezvQuxuhdYRolbv1rft1lOmLzP2z

UtcCMcSYtCOrEWuILx3XBwb3GILPqXDd6cJiQQxmylg8GNftqlnE7u8aJLhlr1ZLRkpncKdj8ERnqcH71wab2HlfUnO9CgXQQxmylg8GNftqlnE7u8aJLhlr1ZLRkpncKdj8ERnqcH71wab2HlfUnO9CgXKd0-CAQC2t3CHt0ku

KoK3nfWQ73s_x4QI69xqzqdDR4tUuNmrv4FC9Yq8vtK5IVm49h_8h6x9X281hAcJcOFDtX7g2BXPvP5O7SOR2V4UI6W8gN6cTJCVAdtWHRRS89Az

Ingat ID proyek, nama unik di semua proyek Google Cloud (nama di atas telah digunakan dan tidak akan bekerja untuk Anda, maaf!). Ini akan disebut nanti dalam codelab ini sebagai PROJECT_ID .

  1. Selanjutnya, Anda harus mengaktifkan penagihan di Cloud Console untuk menggunakan sumber daya Google Cloud.

Menjalankan codelab ini seharusnya tidak memerlukan biaya banyak, jika ada sama sekali. Pastikan untuk mengikuti petunjuk apa pun di bagian "Pembersihan" yang memberi tahu Anda cara mematikan sumber daya sehingga Anda tidak dikenai tagihan setelah tutorial ini. Pengguna baru Google Cloud berhak atas program Uji Coba Gratis senilai $ 300USD .

Aktifkan Cloud Shell

  1. Dari Cloud Console, klik Aktifkan Cloud Shell R47NpBm6yyzso5vnxnRBikeDAXxl3LsM6tip3rJxnKuS2EZdCI0h-eIOGm9aECq8JXbMFlJkd68uTutXU8gGmQUVa5iI1OdZczXP2tzqZ_mj0p .

STsYbcAtkIQyN6nL9BJhld3Fv5KxedYynpUVcRWwvIR-sYMMc4kfK-unIYgtsD4P6T0P8z-A12388tPmAh-Trsx80qobaW4KQXHIciYb

Jika Anda belum pernah memulai Cloud Shell sebelumnya, Anda akan disajikan dengan layar perantara (paro bawah) yang menjelaskan apa itu. Jika demikian, klik Lanjutkan (dan Anda tidak akan pernah melihatnya lagi). Inilah tampilan layar satu kali itu:

LnGMTn1ObgwWFtWpjSdzlA9TDvSbcY76GiLQLc_f7RP1QBK1Tl4H6kLCHzsi89Lkc-serOpqNH-F2XKmV5AnBqTbPon4HvCwSSrY_ERFUQVYmK1SrY_ERFHVYmK

Hanya perlu beberapa saat untuk menyediakan dan terhubung ke Cloud Shell.

hfu9bVHmrWw01Hnrlf4MBNja6yvssDnZzN9oupcG12PU88Vvo30tTluX9IySwnu5_TG3U2UXAasX9eCwqwZtc6Yhwxri95zG82DLvubKxrFYaXnVd7HxnZa

Mesin virtual ini dimuat dengan semua alat pengembangan yang Anda perlukan. Ini menawarkan direktori beranda 5GB yang persisten dan berjalan di Google Cloud, sangat meningkatkan kinerja jaringan dan otentikasi. Banyak, jika tidak semua, pekerjaan Anda di codelab ini dapat dilakukan hanya dengan browser atau Chromebook Anda.

Setelah terhubung ke Cloud Shell, Anda akan melihat bahwa Anda sudah diautentikasi dan bahwa proyek tersebut sudah disetel ke ID proyek Anda.

  1. Jalankan perintah berikut di Cloud Shell untuk mengonfirmasi bahwa Anda diautentikasi:
gcloud auth list

Keluaran perintah

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
gcloud config list project

Keluaran perintah

[core]
project = <PROJECT_ID>

Jika tidak, Anda dapat mengaturnya dengan perintah ini:

gcloud config set project <PROJECT_ID>

Keluaran perintah

Updated property [core/project].

Setelah Cloud Shell diluncurkan, Anda dapat mulai menggunakan gcloud untuk berinteraksi dengan project GCP Anda.

Pertama, aktifkan Cloud Spanner API.

gcloud services enable spanner.googleapis.com

Sekarang, mari buat instance Cloud Spanner yang disebut codelab-instance .

gcloud spanner instances create codelab-instance \
 --config=regional-us-central1 \
 --description="Codelab Instance" --nodes=1

Sekarang, kita perlu menambahkan database ke instance ini. Kami akan menyebutnya codelab-db .

gcloud spanner databases create codelab-db --instance=codelab-instance

Kami akan menggunakan Maven Quickstart Archetype untuk membuat aplikasi konsol Java sederhana.

mvn archetype:generate \
 -DgroupId=codelab \
 -DartifactId=spanner-hibernate-codelab \
 -DarchetypeArtifactId=maven-archetype-quickstart \
 -DarchetypeVersion=1.4 \
 -DinteractiveMode=false

Ubah ke direktori aplikasi.

cd spanner-hibernate-codelab

Kompilasi dan jalankan aplikasi menggunakan Maven.

mvn compile exec:java -Dexec.mainClass=codelab.App

Anda harus melihat Hello World! dicetak di konsol.

Mari jelajahi kode sumber dengan membuka Editor Cloud Shell, dan menjelajahi di dalam direktori spanner-hibernate-codelab .

b5cb37d043d4d2b0.png

Sejauh ini, kami hanya memiliki aplikasi konsol Java dasar yang mencetak "Hello World!" . Namun, kami benar-benar ingin membuat aplikasi Java yang menggunakan Hibernate untuk berbicara dengan Cloud Spanner. Untuk itu, kita memerlukan Cloud Spanner Dialect for Hibernate, driver Cloud Spanner JDBC, dan inti Hibernate. Jadi, mari tambahkan dependensi berikut ke blok <dependencies> di dalam file 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>

Selanjutnya, kita akan membuat file konfigurasi Hibernate hibernate.cfg.xml dan hibernate.properties . Jalankan perintah berikut untuk membuat file kosong, lalu edit menggunakan Editor Cloud Shell.

mkdir src/main/resources \
 && touch src/main/resources/hibernate.cfg.xml \
 && touch src/main/resources/hibernate.properties

Jadi, mari beri tahu Hibernate tentang kelas entitas beranotasi yang akan kita petakan ke database dengan mengisi hibernate.cfg.xml . (Kami akan membuat kelas entitas nanti.)

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>

Hibernasi juga perlu mengetahui cara menyambungkan ke instance Cloud Spanner dan dialek mana yang akan digunakan. Jadi, kami akan memberitahukannya untuk menggunakan SpannerDialect untuk sintaks SQL, driver Spanner JDBC, dan string koneksi JDBC dengan koordinat database. Ini masuk ke file 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

Ingatlah untuk mengganti {PROJECT_ID} dengan ID proyek Anda, yang bisa Anda dapatkan dengan menjalankan perintah berikut:

gcloud config get-value project

Karena kami tidak memiliki skema database, kami menambahkan properti hibernate.hbm2ddl.auto=update agar Hibernate membuat dua tabel di Cloud Spanner saat kami menjalankan aplikasi untuk pertama kali.

Biasanya, Anda juga akan memastikan bahwa kredensial autentikasi sudah disiapkan, menggunakan file JSON akun layanan di variabel lingkungan GOOGLE_APPLICATION_CREDENTIALS atau kredensial default aplikasi yang dikonfigurasi menggunakan perintah gcloud auth application-default login . Namun, karena kami menjalankan Cloud Shell, kredensial project default sudah disiapkan.

Sekarang kami siap untuk menulis beberapa kode.

Kami akan menentukan dua objek Java (POJO) lama biasa yang akan dipetakan ke tabel di Cloud Spanner— Singer dan Album . Album akan memiliki hubungan @ManyToOne dengan Singer . Kami juga dapat memetakan Singer ke daftar Album mereka dengan anotasi @OneToMany , tetapi untuk contoh ini, kami tidak benar-benar ingin memuat semua album setiap kali kami perlu mengambil penyanyi dari database.

Tambahkan kelas entitas Singer dan Album .

Buat file kelas.

touch src/main/java/codelab/Singer.java \
&& touch src/main/java/codelab/Album.java

Tempel konten file.

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;
  }
}

Perhatikan bahwa untuk contoh ini kami menggunakan UUID yang dibuat secara otomatis untuk kunci utama. Ini adalah jenis ID yang disukai di Cloud Spanner, karena menghindari hotspot saat sistem membagi data di antara server menurut rentang kunci. Kunci integer yang meningkat secara monoton juga akan berfungsi, tetapi performanya bisa lebih rendah.

Dengan semua yang dikonfigurasi dan objek entitas ditentukan, kita dapat mulai menulis ke database dan menanyakannya. Kami akan membuka Session Hibernate, dan kemudian menggunakannya untuk terlebih dahulu menghapus semua baris tabel dalam metode clearData() , menyimpan beberapa entitas dalam metode writeData() , dan menjalankan beberapa kueri menggunakan bahasa kueri Hibernate (HQL) di readData() metode.

Ganti konten App.java dengan yang berikut ini:

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;
    }
  }
}

Sekarang, mari kita kompilasi dan jalankan kodenya. Kami akan menambahkan opsi -Dexec.cleanupDaemonThreads=false untuk menyembunyikan peringatan tentang pembersihan untaian daemon yang akan coba dilakukan Maven.

mvn compile exec:java -Dexec.mainClass=codelab.App -Dexec.cleanupDaemonThreads=false

Dalam output Anda akan melihat sesuatu seperti ini:

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

Pada titik ini, jika Anda membuka konsol Cloud Spanner dan melihat data untuk tabel Singer dan Album di database, Anda akan melihat sesuatu seperti ini:

f18276ea54cc266f.png

952d9450dd659e75.png

Mari kita hapus instance Cloud Spanner yang kita buat di awal untuk memastikan bahwa itu tidak menggunakan sumber daya secara tidak perlu.

gcloud spanner instances delete codelab-instance

Selamat, Anda telah berhasil membangun aplikasi Java yang menggunakan Hibernate untuk menyimpan data di Cloud Spanner.

  • Anda membuat instance Cloud Spanner dan database
  • Anda mengonfigurasi aplikasi untuk menggunakan Hibernate
  • Anda membuat dua entitas: Artis dan Album
  • Anda secara otomatis membuat skema database untuk aplikasi Anda
  • Anda berhasil menyimpan entitas ke Cloud Spanner dan membuat kuerinya

Anda sekarang mengetahui langkah-langkah utama yang diperlukan untuk menulis aplikasi Hibernate dengan Cloud Spanner.

Apa berikutnya?