1. Ringkasan
Hibernate telah menjadi solusi ORM standar yang sudah teruji untuk project Java. Hal ini mendukung semua database relasional utama dan memungkinkan alat ORM yang lebih canggih seperti Spring Data JPA. Selain itu, ada banyak framework yang kompatibel dengan Hibernate seperti Spring Boot, Microprofile, dan Quarkus.
Cloud Spanner Dialect for Hibernate ORM memungkinkan penggunaan Hibernate dengan Cloud Spanner. Anda akan mendapatkan manfaat Cloud Spanner - skalabilitas dan semantik relasional - dengan persistensi idiomatis Hibernate. Hal ini dapat membantu Anda memigrasikan aplikasi yang sudah ada ke cloud atau menulis aplikasi baru dengan memanfaatkan peningkatan produktivitas developer yang diberikan oleh teknologi berbasis Hibernate.
Yang akan Anda pelajari
- Cara menulis aplikasi Hibernate sederhana yang terhubung ke Cloud Spanner
- Cara membuat database Cloud Spanner
- Cara menggunakan Dialek Cloud Spanner untuk ORM Hibernate
- Cara menerapkan operasi create-read-update-delete (CRUD) dengan Hibernate
Yang Anda butuhkan
2. Penyiapan dan Persyaratan
Penyiapan lingkungan mandiri
- Login ke Cloud Console dan buat project baru atau gunakan kembali project yang sudah ada. (Jika belum memiliki akun Gmail atau G Suite, Anda harus membuatnya.)
Ingat project ID, nama unik di semua project Google Cloud (maaf, nama di atas telah digunakan dan tidak akan berfungsi untuk Anda!) Project ID tersebut selanjutnya akan dirujuk di codelab ini sebagai PROJECT_ID
.
- Selanjutnya, Anda harus mengaktifkan penagihan di Cloud Console untuk menggunakan resource Google Cloud.
Menjalankan operasi dalam codelab ini seharusnya tidak memerlukan banyak biaya, bahkan mungkin tidak sama sekali. Pastikan untuk mengikuti petunjuk yang ada di bagian "Membersihkan" yang memberi tahu Anda cara menonaktifkan resource sehingga tidak menimbulkan penagihan di luar tutorial ini. Pengguna baru Google Cloud memenuhi syarat untuk mengikuti program Uji Coba Gratis senilai $300 USD.
Mengaktifkan Cloud Shell
- Dari Cloud Console, klik Aktifkan Cloud Shell .
Jika belum pernah memulai Cloud Shell, Anda akan melihat layar perantara (di paruh bawah) yang menjelaskan apa itu Cloud Shell. Jika demikian, klik Lanjutkan (dan Anda tidak akan pernah melihatnya lagi). Berikut tampilan layar sekali-tampil tersebut:
Perlu waktu beberapa saat untuk penyediaan dan terhubung ke Cloud Shell.
Mesin virtual ini berisi semua alat pengembangan yang Anda perlukan. Layanan ini menawarkan direktori beranda tetap sebesar 5 GB dan beroperasi di Google Cloud, sehingga sangat meningkatkan performa dan autentikasi jaringan. Sebagian besar pekerjaan Anda dalam codelab ini dapat dilakukan hanya dengan browser atau Chromebook.
Setelah terhubung ke Cloud Shell, Anda akan melihat bahwa Anda sudah diautentikasi dan project sudah ditetapkan ke project ID Anda.
- Jalankan perintah berikut di Cloud Shell untuk mengonfirmasi bahwa Anda telah diautentikasi:
gcloud auth list
Output 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
Output perintah
[core] project = <PROJECT_ID>
Jika tidak, Anda dapat menyetelnya dengan perintah ini:
gcloud config set project <PROJECT_ID>
Output perintah
Updated property [core/project].
3. Buat database
Setelah Cloud Shell diluncurkan, Anda dapat mulai menggunakan gcloud
untuk berinteraksi dengan project GCP.
Pertama, aktifkan Cloud Spanner API.
gcloud services enable spanner.googleapis.com
Sekarang, mari kita buat instance Cloud Spanner bernama 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. Kita akan menyebutnya codelab-db
.
gcloud spanner databases create codelab-db --instance=codelab-instance
4. Membuat aplikasi kosong
Kita akan menggunakan Arketipe Panduan Memulai Maven 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
Mengompilasi dan menjalankan aplikasi menggunakan Maven.
mvn compile exec:java -Dexec.mainClass=codelab.App
Anda akan melihat Hello World!
yang dicetak di konsol.
5. Menambahkan dependensi
Mari kita pelajari kode sumber dengan membuka Cloud Shell Editor, dan menjelajahi di dalam direktori spanner-hibernate-codelab
.
Sejauh ini, kita baru saja memiliki aplikasi konsol Java dasar yang mencetak "Hello World!"
. Namun, kita ingin menulis aplikasi Java yang menggunakan Hibernate untuk berkomunikasi dengan Cloud Spanner. Untuk itu, kita memerlukan Cloud Spanner Dialect untuk Hibernate, driver JDBC Cloud Spanner, dan Hibernate core. Jadi, mari kita 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>
6. Mengonfigurasi ORM Hibernasi
Selanjutnya, kita akan membuat file konfigurasi Hibernate hibernate.cfg.xml
dan hibernate.properties
. Jalankan perintah berikut untuk membuat file kosong, lalu edit menggunakan Cloud Shell Editor.
mkdir src/main/resources \ && touch src/main/resources/hibernate.cfg.xml \ && touch src/main/resources/hibernate.properties
Jadi, beri tahu Hibernate tentang class entity yang dianotasi dan akan kita petakan ke database dengan mengisi hibernate.cfg.xml
. (Kita akan membuat class entity 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>
Hibernate juga perlu mengetahui cara terhubung ke instance Cloud Spanner dan dialek mana yang akan digunakan. Jadi, kita akan memintanya menggunakan SpannerDialect
untuk sintaksis SQL, driver JDBC Spanner, dan string koneksi JDBC dengan koordinat database. File ini akan dimasukkan 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
Jangan lupa untuk mengganti {PROJECT_ID}
dengan project ID Anda, yang bisa Anda dapatkan dengan menjalankan perintah berikut:
gcloud config get-value project
Karena belum memiliki skema database, kami menambahkan properti hibernate.hbm2ddl.auto=update
agar Hibernate dapat membuat dua tabel di Cloud Spanner saat menjalankan aplikasi untuk pertama kalinya.
Biasanya, Anda juga akan memastikan bahwa kredensial autentikasi telah disiapkan, menggunakan file JSON akun layanan dalam variabel lingkungan GOOGLE_APPLICATION_CREDENTIALS
atau kredensial default aplikasi yang dikonfigurasi menggunakan perintah gcloud auth application-default login
. Namun, karena kita berjalan di Cloud Shell, kredensial project default sudah disiapkan.
7. Membuat class entity yang telah dianotasi
Sekarang kita siap untuk menulis beberapa kode.
Kita akan menentukan dua objek Java lama (POJO) biasa yang akan dipetakan ke tabel di Cloud Spanner—Singer
dan Album
. Album
akan memiliki hubungan @ManyToOne
dengan Singer
. Kita juga dapat memetakan Singer
ke daftar Album
dengan anotasi @OneToMany
, tetapi untuk contoh ini, kita tidak ingin memuat semua album setiap kali kita perlu mengambil penyanyi dari database.
Tambahkan class entity Singer
dan Album
.
Membuat file kelas.
touch src/main/java/codelab/Singer.java \ && touch src/main/java/codelab/Album.java
Tempelkan 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, kita menggunakan UUID yang dibuat secara otomatis untuk kunci utama. Ini adalah jenis ID yang lebih disukai di Cloud Spanner, karena menghindari hotspot saat sistem membagi data di antara server berdasarkan rentang kunci. Kunci bilangan bulat yang meningkat secara monoton juga akan berfungsi, tetapi performanya mungkin kurang baik.
8. Menyimpan dan membuat kueri entity
Setelah semua hal yang telah dikonfigurasi dan objek entity telah ditentukan, kita dapat mulai menulis ke database dan membuat kuerinya. Kita akan membuka Session
Hibernasi, lalu menggunakannya untuk menghapus semua baris tabel dalam metode clearData()
terlebih dahulu, menyimpan beberapa entity dalam metode writeData()
, dan menjalankan beberapa kueri menggunakan bahasa kueri Hibernate (HQL) dalam metode readData()
.
Ganti konten App.java
dengan konten berikut:
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. Kita akan menambahkan opsi -Dexec.cleanupDaemonThreads=false
untuk menyembunyikan peringatan tentang pembersihan thread daemon yang akan coba dilakukan Maven.
mvn compile exec:java -Dexec.mainClass=codelab.App -Dexec.cleanupDaemonThreads=false
Di 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 tahap ini, jika Anda membuka konsol Cloud Spanner dan melihat data untuk tabel Penyanyi dan Album dalam database, Anda akan melihat data seperti ini:
9. Pembersihan
Mari kita hapus instance Cloud Spanner yang kita buat di awal untuk memastikan bahwa instance tersebut tidak menghabiskan resource secara tidak perlu.
gcloud spanner instances delete codelab-instance
10. Selamat
Selamat, Anda telah berhasil membangun aplikasi Java yang menggunakan Hibernate untuk mempertahankan data di Cloud Spanner.
- Anda telah membuat instance dan database Cloud Spanner
- Anda telah mengonfigurasi aplikasi untuk menggunakan Hibernate
- Anda membuat dua entitas: Artis dan Album
- Anda secara otomatis membuat skema database untuk aplikasi
- Anda berhasil menyimpan entity ke Cloud Spanner dan membuat kuerinya
Anda sekarang mengetahui langkah-langkah utama yang diperlukan untuk menulis aplikasi Hibernate dengan Cloud Spanner.