1. Omówienie
Hibernate jest obecnie standardowym rozwiązaniem ORM do projektów w języku Java. Obsługuje wszystkie główne relacyjne bazy danych i umożliwia korzystanie z jeszcze bardziej zaawansowanych narzędzi ORM, takich jak Spring Data JPA. Dodatkowo istnieje wiele platform zgodnych z hibernate, takich jak Spring Boot, Microprofile i Quarkus.
Cloud Spanner Dialect for Hibernate ORM umożliwia korzystanie z hibernate w usłudze Cloud Spanner. Masz dostęp do zalet usługi Cloud Spanner – skalowalności i semantyki relacyjnej – dzięki idiomatycznemu zachowaniu architektury Hibernate. Dzięki temu możesz przenieść istniejące aplikacje do chmury lub napisać nowe, wykorzystując zwiększoną produktywność programistów, jaką zapewnia technologie oparte na Hibernate.
Czego się nauczysz
- Jak napisać prostą aplikację Hibernate, która łączy się z Cloud Spanner
- Jak utworzyć bazę danych Cloud Spanner
- Jak używać Dialect Cloud Spanner do hibernacji ORM
- Jak wdrożyć operacje CRUD (create-read-update-delete) za pomocą Hibernate
Czego potrzebujesz
2. Konfiguracja i wymagania
Samodzielne konfigurowanie środowiska
- Zaloguj się w konsoli Cloud i utwórz nowy projekt lub wykorzystaj już istniejący. Jeśli nie masz jeszcze konta Gmail lub G Suite, musisz je utworzyć.
Zapamiętaj identyfikator projektu, unikalną nazwę we wszystkich projektach Google Cloud (powyższa nazwa jest już zajęta i nie będzie Ci odpowiadać). W dalszej części tego ćwiczenia w programie będzie ona określana jako PROJECT_ID
.
- Następnie musisz włączyć płatności w Cloud Console, aby korzystać z zasobów Google Cloud.
Ukończenie tego ćwiczenia z programowania nie powinno kosztować zbyt wiele. Postępuj zgodnie z instrukcjami podanymi w sekcji „Czyszczenie” W tym samouczku znajdziesz wskazówki, jak wyłączyć zasoby, aby uniknąć naliczania opłat. Nowi użytkownicy Google Cloud mogą skorzystać z programu bezpłatnego okresu próbnego o wartości 300 USD.
Aktywowanie Cloud Shell
- W konsoli Cloud kliknij Aktywuj Cloud Shell
.
Jeśli dopiero zaczynasz korzystać z Cloud Shell, wyświetli się ekran pośredni (w części strony widocznej po przewinięciu) z opisem tej funkcji. W takim przypadku kliknij Dalej (nie zobaczysz go więcej). Tak wygląda ten jednorazowy ekran:
Uzyskanie dostępu do Cloud Shell i połączenie się z nim powinno zająć tylko kilka chwil.
Ta maszyna wirtualna ma wszystkie potrzebne narzędzia dla programistów. Zawiera stały katalog domowy o pojemności 5 GB i działa w Google Cloud, co znacznie zwiększa wydajność sieci i uwierzytelnianie. Większość czynności z tego ćwiczenia z programowania można wykonać w przeglądarce lub na Chromebooku.
Po nawiązaniu połączenia z Cloud Shell powinno pojawić się informacja, że użytkownik jest już uwierzytelniony i że projekt jest już ustawiony na identyfikator Twojego projektu.
- Uruchom to polecenie w Cloud Shell, aby potwierdzić, że jesteś uwierzytelniony:
gcloud auth list
Dane wyjściowe polecenia
Credentialed Accounts ACTIVE ACCOUNT * <my_account>@<my_domain.com> To set the active account, run: $ gcloud config set account `ACCOUNT`
gcloud config list project
Dane wyjściowe polecenia
[core] project = <PROJECT_ID>
Jeśli tak nie jest, możesz go ustawić za pomocą tego polecenia:
gcloud config set project <PROJECT_ID>
Dane wyjściowe polecenia
Updated property [core/project].
3. Utwórz bazę danych
Po uruchomieniu Cloud Shell możesz zacząć używać narzędzia gcloud
do interakcji z projektem GCP.
Najpierw włącz interfejs Cloud Spanner API.
gcloud services enable spanner.googleapis.com
Teraz utwórzmy instancję Cloud Spanner o nazwie codelab-instance
.
gcloud spanner instances create codelab-instance \ --config=regional-us-central1 \ --description="Codelab Instance" --nodes=1
Teraz musimy dodać do tej instancji bazę danych. Nazwiemy ją codelab-db
.
gcloud spanner databases create codelab-db --instance=codelab-instance
4. Tworzenie pustej aplikacji
Wykorzystamy archetyp krótkiego wprowadzenia Maven do utworzenia prostej aplikacji konsoli Java.
mvn archetype:generate \ -DgroupId=codelab \ -DartifactId=spanner-hibernate-codelab \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DarchetypeVersion=1.4 \ -DinteractiveMode=false
Przejdź do katalogu aplikacji.
cd spanner-hibernate-codelab
Skompiluj i uruchom aplikację za pomocą Maven.
mvn compile exec:java -Dexec.mainClass=codelab.App
W konsoli powinien pojawić się numer Hello World!
.
5. Dodaj zależności
Aby poznać kod źródłowy, otwórz edytor Cloud Shell i przejrzyjmy katalog spanner-hibernate-codelab
.
Do tej pory mamy tylko podstawową aplikację konsoli Java, która drukuje wersję "Hello World!"
. Chcemy jednak napisać aplikację w języku Java, która używa Hibernate do komunikacji z Cloud Spanner. Do tego będą potrzebne Dialect Cloud Spanner dla Hibernate, sterownik JDBC Cloud Spanner i rdzeń Hibernate. Dodajmy więc następujące zależności do bloku <dependencies>
w pliku 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. Konfigurowanie Hibernate ORM
Następnie utworzymy pliki konfiguracji Hibernate hibernate.cfg.xml
i hibernate.properties
. Uruchom to polecenie, aby utworzyć puste pliki, a następnie zmodyfikuj je za pomocą edytora Cloud Shell.
mkdir src/main/resources \ && touch src/main/resources/hibernate.cfg.xml \ && touch src/main/resources/hibernate.properties
Wypełnijmy pole hibernate.cfg.xml
, by więc opowiedzieć usłudze Hibernate o klasach encji z adnotacjami, które będziemy mapować na bazę danych. (klasy encji utworzymy później).
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 musi też wiedzieć, jak nawiązać połączenie z instancją Cloud Spanner i którego dialektu ma używać. Dlatego polecimy użycie SpannerDialect
w przypadku składni SQL, sterownika JDBC Spannera i ciągu połączenia JDBC ze współrzędnymi bazy danych. Zostanie ono umieszczone w pliku 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
Pamiętaj, aby zastąpić {PROJECT_ID}
identyfikatorem projektu, który można uzyskać, uruchamiając to polecenie:
gcloud config get-value project
Ponieważ nie mamy schematu bazy danych, dodaliśmy właściwość hibernate.hbm2ddl.auto=update
, aby umożliwić usłudze Hibernate utworzenie 2 tabel w usłudze Cloud Spanner przy pierwszym uruchomieniu aplikacji.
Aby się upewnić, że dane uwierzytelniające są też skonfigurowane, możesz użyć pliku JSON konta usługi w zmiennej środowiskowej GOOGLE_APPLICATION_CREDENTIALS
lub domyślnych danych uwierzytelniających aplikacji skonfigurowanych za pomocą polecenia gcloud auth application-default login
. Ponieważ jednak działamy w Cloud Shell, domyślne dane logowania do projektu są już skonfigurowane.
7. Tworzenie klas encji z adnotacjami
Teraz możemy napisać trochę kodu.
Zdefiniujemy 2 stare obiekty Java (POJO), które zostaną zmapowane na tabele w Cloud Spanner – Singer
i Album
. Element Album
będzie miał relację @ManyToOne
z usługą Singer
. Mogliśmy też zmapować albumy Singer
na listy ich elementów Album
z adnotacją @OneToMany
, ale w tym przykładzie nie chcemy wczytywać wszystkich albumów za każdym razem, gdy musimy pobrać piosenkarza z bazy danych.
Dodaj klasy elementu Singer
i Album
.
Utwórz pliki zajęć.
touch src/main/java/codelab/Singer.java \ && touch src/main/java/codelab/Album.java
Wklej zawartość plików.
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;
}
}
Zwróć uwagę, że w tym przykładzie używamy automatycznie wygenerowanego identyfikatora UUID klucza podstawowego. Jest to preferowany typ identyfikatora w Cloud Spanner, ponieważ pozwala uniknąć hotspotów, ponieważ system dzieli dane między serwerami według zakresów kluczy. Monotonicznie rosnący klucz całkowity również zadziałał, ale może być mniej wydajny.
8. Zapisz i odpytuj encje
Po skonfigurowaniu wszystkich obiektów i zdefiniowaniu obiektów encji możemy zacząć zapisywać dane w bazie danych i wysyłać do niej zapytania. Otworzymy tabelę Session
, a następnie użyjemy jej, aby najpierw usunąć wszystkie wiersze tabeli w metodzie clearData()
, zapisać niektóre encje w metodzie writeData()
i uruchomić kilka zapytań z użyciem języka zapytań Hibernate (HQL) w metodzie readData()
.
Zamień zawartość pliku App.java
na taką:
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;
}
}
}
Teraz skompilujmy i uruchommy kod. Dodamy opcję -Dexec.cleanupDaemonThreads=false
, aby pomijać ostrzeżenia o czyszczeniu wątków demonów, które będzie próbował wykonać Maven.
mvn compile exec:java -Dexec.mainClass=codelab.App -Dexec.cleanupDaemonThreads=false
W danych wyjściowych powinny pojawić się coś takiego:
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
Jeśli w tej chwili otworzysz konsolę Cloud Spanner i wyświetlisz w bazie danych tabele piosenkarz i album, zobaczysz coś takiego:
9. Czyszczenie danych
Usuńmy instancję Cloud Spanner, którą utworzyliśmy na początku, aby mieć pewność, że nie wykorzystuje niepotrzebnie zasobów.
gcloud spanner instances delete codelab-instance
10. Gratulacje
Gratulujemy! Udało Ci się utworzyć aplikację w Javie, która używa Hibernate do przechowywania danych w Cloud Spanner.
- Udało Ci się utworzyć instancję Cloud Spanner i bazę danych
- Aplikacja została skonfigurowana tak, aby używała programu Hibernate
- Zostały przez Ciebie utworzone 2 elementy: Wykonawca i Album
- Schemat bazy danych dla Twojej aplikacji został wygenerowany automatycznie
- Udało Ci się zapisać encje w Cloud Spanner i wysłać do nich zapytanie
Znasz już najważniejsze kroki wymagane do napisania aplikacji Hibernate za pomocą Cloud Spanner.