1. ภาพรวม
Hibernate กลายเป็นโซลูชัน ORM มาตรฐานโดยพฤตินัยสำหรับโปรเจ็กต์ Java โดยรองรับฐานข้อมูลเชิงสัมพันธ์หลักๆ ทั้งหมด และช่วยให้ใช้เครื่องมือ ORM ที่มีประสิทธิภาพมากยิ่งขึ้น เช่น Spring Data JPA นอกจากนี้ ยังมีเฟรมเวิร์กที่ใช้ได้กับ Hibernate อีกมากมาย เช่น Spring Boot, Microprofile และ Quarkus
Cloud Spanner Dialect สำหรับ Hibernate ORM ช่วยให้ใช้ Hibernate กับ Cloud Spanner ได้ คุณจะได้รับประโยชน์จาก Cloud Spanner ซึ่งก็คือความสามารถในการปรับขนาดและความหมายเชิงสัมพันธ์ พร้อมด้วยการคงอยู่ของ Hibernate ซึ่งจะช่วยให้คุณย้ายข้อมูลแอปพลิเคชันที่มีอยู่ไปยังระบบคลาวด์หรือเขียนแอปพลิเคชันใหม่โดยใช้ประโยชน์จากประสิทธิภาพการทำงานของนักพัฒนาแอปที่เพิ่มขึ้นซึ่งเทคโนโลยีที่ใช้ Hibernate มอบให้
สิ่งที่คุณจะได้เรียนรู้
- วิธีเขียนแอปพลิเคชัน Hibernate อย่างง่ายที่เชื่อมต่อกับ Cloud Spanner
- วิธีสร้างฐานข้อมูล Cloud Spanner
- วิธีใช้ภาษา SQL ของ Cloud Spanner สำหรับ Hibernate ORM
- วิธีใช้การดำเนินการสร้าง-อ่าน-อัปเดต-ลบ (CRUD) ด้วย Hibernate
สิ่งที่คุณต้องมี
2. การตั้งค่าและข้อกำหนด
การตั้งค่าสภาพแวดล้อมแบบเรียนรู้ด้วยตนเอง
- ลงชื่อเข้าใช้ Cloud Console แล้วสร้างโปรเจ็กต์ใหม่หรือใช้โปรเจ็กต์ที่มีอยู่ซ้ำ (หากยังไม่มีบัญชี Gmail หรือ G Suite คุณต้องสร้างบัญชี)
โปรดจดจำรหัสโปรเจ็กต์ ซึ่งเป็นชื่อที่ไม่ซ้ำกันในโปรเจ็กต์ Google Cloud ทั้งหมด (ชื่อด้านบนมีผู้ใช้แล้วและจะใช้ไม่ได้ ขออภัย) ซึ่งจะเรียกว่า PROJECT_ID ในภายหลังใน Codelab นี้
- จากนั้นคุณจะต้องเปิดใช้การเรียกเก็บเงินใน Cloud Console เพื่อใช้ทรัพยากร Google Cloud
การทำตาม Codelab นี้ไม่ควรมีค่าใช้จ่ายมากนัก หรืออาจไม่มีเลย โปรดทำตามวิธีการในส่วน "การล้างข้อมูล" ซึ่งจะแนะนำวิธีปิดทรัพยากรเพื่อไม่ให้มีการเรียกเก็บเงินนอกเหนือจากบทแนะนำนี้ ผู้ใช้ Google Cloud รายใหม่มีสิทธิ์เข้าร่วมโปรแกรมช่วงทดลองใช้ฟรีมูลค่า$300 USD
เปิดใช้งาน Cloud Shell
- จาก Cloud Console ให้คลิกเปิดใช้งาน Cloud Shell
หากไม่เคยเริ่มใช้ Cloud Shell มาก่อน คุณจะเห็นหน้าจอระดับกลาง (ด้านล่าง) ที่อธิบายว่า Cloud Shell คืออะไร ในกรณีนี้ ให้คลิกต่อไป (และคุณจะไม่เห็นหน้าจอนี้อีก) หน้าจอแบบครั้งเดียวจะมีลักษณะดังนี้
การจัดสรรและเชื่อมต่อกับ Cloud Shell จะใช้เวลาไม่นาน
เครื่องเสมือนนี้มาพร้อมเครื่องมือพัฒนาซอฟต์แวร์ทั้งหมดที่คุณต้องการ โดยมีไดเรกทอรีหลักแบบถาวรขนาด 5 GB และทำงานใน 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. เพิ่มทรัพยากร Dependency
มาสำรวจซอร์สโค้ดโดยเปิด Cloud Shell Editor และเรียกดูภายในไดเรกทอรี spanner-hibernate-codelab กัน

ตอนนี้เรามีเพียงแอปคอนโซล Java พื้นฐานที่พิมพ์ "Hello World!" อย่างไรก็ตาม เราต้องการเขียนแอปพลิเคชัน Java ที่ใช้ Hibernate เพื่อสื่อสารกับ Cloud Spanner เราจึงต้องใช้ Cloud Spanner Dialect สำหรับ Hibernate, ไดรเวอร์ JDBC ของ Cloud Spanner และ Hibernate Core ดังนั้น ให้เพิ่มทรัพยากร Dependency ต่อไปนี้ลงในบล็อก <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, ไดรเวอร์ 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 สร้างตาราง 2 ตารางใน Cloud Spanner เมื่อเราเรียกใช้แอปเป็นครั้งแรก
โดยปกติแล้ว คุณจะต้องตรวจสอบว่าได้ตั้งค่าข้อมูลเข้าสู่ระบบการตรวจสอบสิทธิ์แล้ว โดยใช้ไฟล์ JSON ของบัญชีบริการในตัวแปรสภาพแวดล้อม GOOGLE_APPLICATION_CREDENTIALS หรือข้อมูลเข้าสู่ระบบเริ่มต้นของแอปพลิเคชันที่กำหนดค่าโดยใช้คำสั่ง gcloud auth application-default login อย่างไรก็ตาม เนื่องจากเรากำลังเรียกใช้ใน Cloud Shell ระบบจึงตั้งค่าข้อมูลเข้าสู่ระบบของโปรเจ็กต์เริ่มต้นไว้แล้ว
7. สร้างคลาสเอนทิตีที่มีคำอธิบายประกอบ
ตอนนี้เราพร้อมที่จะเขียนโค้ดแล้ว
เราจะกำหนดออบเจ็กต์ Java แบบธรรมดา (POJO) 2 รายการที่จะแมปกับตารางใน 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. บันทึกและค้นหาเอนทิตี
เมื่อกำหนดค่าทุกอย่างและกำหนดออบเจ็กต์เอนทิตีแล้ว เราจะเริ่มเขียนลงในฐานข้อมูลและค้นหาได้ เราจะเปิด Hibernate Session จากนั้นใช้เพื่อลบแถวตารางทั้งหมดในเมธอด 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 ในฐานข้อมูล คุณจะเห็นข้อมูลคล้ายกับตัวอย่างต่อไปนี้


9. ล้างข้อมูล
มาลบอินสแตนซ์ Cloud Spanner ที่เราสร้างไว้ตอนต้นเพื่อให้แน่ใจว่าอินสแตนซ์จะไม่ใช้ทรัพยากรโดยไม่จำเป็น
gcloud spanner instances delete codelab-instance
10. ขอแสดงความยินดี
ขอแสดงความยินดี คุณสร้างแอปพลิเคชัน Java ที่ใช้ Hibernate เพื่อบันทึกข้อมูลใน Cloud Spanner ได้สำเร็จแล้ว
- คุณสร้างอินสแตนซ์และฐานข้อมูล Cloud Spanner แล้ว
- คุณกำหนดค่าแอปพลิเคชันให้ใช้ Hibernate
- คุณสร้างเอนทิตี 2 รายการ ได้แก่ ศิลปินและอัลบั้ม
- คุณสร้างสคีมาฐานข้อมูลสำหรับแอปพลิเคชันโดยอัตโนมัติ
- คุณบันทึกเอนทิตีลงใน Cloud Spanner และค้นหาเอนทิตีเหล่านั้นได้สำเร็จแล้ว
ตอนนี้คุณทราบขั้นตอนสำคัญที่จำเป็นในการเขียนแอปพลิเคชัน Hibernate ด้วย Cloud Spanner แล้ว