1. Panoramica
Hibernate è diventata di fatto la soluzione ORM standard per i progetti Java. Supporta tutti i principali database relazionali e consente strumenti ORM ancora più potenti come Spring Data JPA. Inoltre, sono disponibili molti framework compatibili con Hibernate, ad esempio Spring Boot, Microprofile e Quarkus.
Cloud Spanner Dialect for Hibernate ORM consente di utilizzare Hibernate con Cloud Spanner. Ottieni i vantaggi di Cloud Spanner (scalabilità e semantica relazionale) con la persistenza idiomatica di Hibernate. Questo può aiutarti a migrare le applicazioni esistenti nel cloud o a scriverne di nuove sfruttando la maggiore produttività degli sviluppatori offerta dalle tecnologie basate su Hibernate.
Cosa imparerai a fare
- Come scrivere una semplice applicazione Hibernate che si connetta a Cloud Spanner
- Creare un database Cloud Spanner
- Come utilizzare il dialetto Cloud Spanner per l'ORM Hibernate
- Come implementare le operazioni create-read-update-delete (CRUD) con Hibernate
Che cosa ti serve
2. Configurazione e requisiti
Configurazione dell'ambiente da seguire in modo autonomo
- Accedi alla console Cloud e crea un nuovo progetto o riutilizzane uno esistente. Se non hai ancora un account Gmail o G Suite, devi crearne uno.
Ricorda l'ID progetto, un nome univoco in tutti i progetti Google Cloud (il nome precedente è già stato utilizzato e non funzionerà correttamente). Verrà indicato più avanti in questo codelab come PROJECT_ID
.
- Successivamente, dovrai abilitare la fatturazione in Cloud Console per utilizzare le risorse Google Cloud.
Eseguire questo codelab non dovrebbe costare molto. Assicurati di seguire le istruzioni nella sezione "Pulizia" in cui viene spiegato come arrestare le risorse in modo da non incorrere in fatturazione oltre questo tutorial. I nuovi utenti di Google Cloud sono idonei al programma di prova senza costi di 300$.
Attiva Cloud Shell
- Dalla console Cloud, fai clic su Attiva Cloud Shell .
Se non hai mai avviato Cloud Shell, verrà visualizzata una schermata intermedia (below the fold) in cui viene descritto di cosa si tratta. In tal caso, fai clic su Continua (e non la vedrai più). Ecco come appare quella singola schermata:
Il provisioning e la connessione a Cloud Shell dovrebbero richiedere solo qualche istante.
Questa macchina virtuale viene caricata con tutti gli strumenti di sviluppo necessari. Offre una home directory permanente da 5 GB e viene eseguita in Google Cloud, migliorando notevolmente le prestazioni di rete e l'autenticazione. Gran parte, se non tutto, del lavoro in questo codelab può essere svolto semplicemente con un browser o Chromebook.
Una volta eseguita la connessione a Cloud Shell, dovresti vedere che il tuo account è già autenticato e il progetto è già impostato sul tuo ID progetto.
- Esegui questo comando in Cloud Shell per verificare che l'account sia autenticato:
gcloud auth list
Output comando
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 comando
[core] project = <PROJECT_ID>
In caso contrario, puoi impostarlo con questo comando:
gcloud config set project <PROJECT_ID>
Output comando
Updated property [core/project].
3. Crea un database
Dopo il lancio di Cloud Shell, puoi iniziare a utilizzare gcloud
per interagire con il tuo progetto Google Cloud.
Innanzitutto, abilita l'API Cloud Spanner.
gcloud services enable spanner.googleapis.com
Ora creiamo un'istanza Cloud Spanner denominata codelab-instance
.
gcloud spanner instances create codelab-instance \ --config=regional-us-central1 \ --description="Codelab Instance" --nodes=1
Ora dobbiamo aggiungere un database a questa istanza. Lo chiameremo codelab-db
.
gcloud spanner databases create codelab-db --instance=codelab-instance
4. Crea un'app vuota
Utilizzeremo l'archetype della guida rapida Maven per creare una semplice applicazione per console Java.
mvn archetype:generate \ -DgroupId=codelab \ -DartifactId=spanner-hibernate-codelab \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DarchetypeVersion=1.4 \ -DinteractiveMode=false
Passa alla directory dell'app.
cd spanner-hibernate-codelab
Compila ed esegui l'app utilizzando Maven.
mvn compile exec:java -Dexec.mainClass=codelab.App
Dovresti vedere Hello World!
stampato sulla console.
5. Aggiungi dipendenze
Esploriamo il codice sorgente aprendo l'editor di Cloud Shell e sfogliando la directory spanner-hibernate-codelab
.
Finora abbiamo solo un'app console Java di base che stampa "Hello World!"
. Tuttavia, vogliamo davvero scrivere un'applicazione Java che utilizzi Hibernate per comunicare con Cloud Spanner. Per farlo, abbiamo bisogno del dialetto Cloud Spanner per Hibernate, del driver JDBC di Cloud Spanner e del core Hibernate. Aggiungiamo quindi le seguenti dipendenze al blocco <dependencies>
all'interno del 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. Configura Hibernate ORM
Poi creeremo i file di configurazione Hibernate hibernate.cfg.xml
e hibernate.properties
. Esegui questo comando per creare i file vuoti, quindi modificali utilizzando l'editor di Cloud Shell.
mkdir src/main/resources \ && touch src/main/resources/hibernate.cfg.xml \ && touch src/main/resources/hibernate.properties
Quindi, comunichiamo a Hibernate le classi di entità annotate che mapperemo al database compilando hibernate.cfg.xml
. Creeremo le classi entità più tardi.
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 deve inoltre sapere come connettersi all'istanza Cloud Spanner e quale dialetto utilizzare. Pertanto, gli specifichiamo di utilizzare SpannerDialect
per la sintassi SQL, il driver JDBC di Spanner e la stringa di connessione JDBC con le coordinate del database. Questo nome verrà inserito nel 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
Ricorda di sostituire {PROJECT_ID}
con il tuo ID progetto, che puoi ottenere eseguendo questo comando:
gcloud config get-value project
Poiché non esiste uno schema di database, abbiamo aggiunto la proprietà hibernate.hbm2ddl.auto=update
per consentire a Hibernate di creare le due tabelle in Cloud Spanner quando eseguiamo l'app per la prima volta.
In genere, ti conviene anche assicurarti che siano configurate le credenziali di autenticazione, utilizzando un file JSON dell'account di servizio nella variabile di ambiente GOOGLE_APPLICATION_CREDENTIALS
o le credenziali predefinite dell'applicazione configurate utilizzando il comando gcloud auth application-default login
. Tuttavia, poiché viene eseguito in Cloud Shell, le credenziali di progetto predefinite sono già configurate.
7. crea classi di entità annotate
Ora siamo pronti a scrivere del codice.
Definiamo due semplici oggetti Java (POJO) che verranno mappati alle tabelle in Cloud Spanner: Singer
e Album
. Album
avrà una relazione @ManyToOne
con Singer
. Potremmo anche aver mappato i Singer
agli elenchi dei loro Album
con un'annotazione @OneToMany
, ma per questo esempio non vogliamo caricare tutti gli album ogni volta che dobbiamo recuperare un cantante dal database.
Aggiungi le classi di entità Singer
e Album
.
Crea i file del corso.
touch src/main/java/codelab/Singer.java \ && touch src/main/java/codelab/Album.java
Incolla i contenuti dei 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;
}
}
Tieni presente che per questo esempio utilizziamo un UUID generato automaticamente per la chiave primaria. Si tratta di un tipo di ID preferito in Cloud Spanner, perché evita gli hotspot mentre il sistema divide i dati tra i server in base a intervalli di chiavi. Funzionerebbe anche una chiave intero monotonica che aumenta in modo monotonico, ma le prestazioni sono inferiori.
8. Salvare ed eseguire query sulle entità
Una volta definiti tutti gli oggetti configurati e le entità, possiamo iniziare a scrivere sul database ed eseguirvi query. Apriremo un Session
Hibernate, quindi lo utilizzeremo per eliminare prima tutte le righe di tabella nel metodo clearData()
, salvare alcune entità nel metodo writeData()
ed eseguire alcune query utilizzando Hibernate Query Language (HQL) nel metodo readData()
.
Sostituisci i contenuti di App.java
come segue:
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;
}
}
}
Ora compiliamo ed eseguiamo il codice. Aggiungeremo l'opzione -Dexec.cleanupDaemonThreads=false
per eliminare gli avvisi sulla pulizia dei thread del daemon che Maven proverà a eseguire.
mvn compile exec:java -Dexec.mainClass=codelab.App -Dexec.cleanupDaemonThreads=false
Nell'output dovresti vedere qualcosa di simile a questo:
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
A questo punto, se vai alla console Cloud Spanner e visualizzi i dati relativi alle tabelle Cantante e Album nel database, vedrai quanto segue:
9. Esegui la pulizia
Eliminamo l'istanza Cloud Spanner che abbiamo creato all'inizio per assicurarci che non utilizzi risorse inutilmente.
gcloud spanner instances delete codelab-instance
10. Complimenti
Congratulazioni, hai creato un'applicazione Java che utilizza Hibernate per rendere persistenti i dati in Cloud Spanner.
- Hai creato un'istanza Cloud Spanner e un database
- Hai configurato l'applicazione per l'utilizzo di Hibernate
- Hai creato due entità: Artista e Album
- Hai generato automaticamente lo schema del database per la tua applicazione
- Hai salvato le entità in Cloud Spanner ed hai eseguito query
Ora conosci i passaggi chiave necessari per scrivere un'applicazione Hibernate con Cloud Spanner.