1. Panoramica
Hibernate è diventata la soluzione ORM standard di fatto per i progetti Java. Supporta tutti i principali database relazionali e consente di utilizzare strumenti ORM ancora più potenti come Spring Data JPA. Inoltre, esistono molti framework compatibili con Hibernate, come Spring Boot, Microprofile e Quarkus.
Cloud Spanner Dialect for Hibernate ORM consente di utilizzare Hibernate con Cloud Spanner. Puoi usufruire dei vantaggi di Cloud Spanner, ovvero scalabilità e semantica relazionale, con la persistenza idiomatica di Hibernate. Questo può aiutarti a eseguire la migrazione delle applicazioni esistenti al 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 connette a Cloud Spanner
- Come creare un database Cloud Spanner
- Come utilizzare Cloud Spanner Dialect for Hibernate ORM
- Come implementare le operazioni di creazione, lettura, aggiornamento ed eliminazione (CRUD) con Hibernate
Che cosa ti serve
2. Configurazione e requisiti
Configurazione dell'ambiente autonomo
- Accedi a Cloud Console e crea un nuovo progetto o riutilizzane uno esistente. (Se non hai già un account Gmail o G Suite, devi crearne uno.)
Ricorda l'ID progetto, un nome univoco tra tutti i progetti Google Cloud (il nome sopra è già stato utilizzato e non funzionerà per te, ci dispiace!). In questo codelab verrà chiamato PROJECT_ID.
- Dopodiché, devi abilitare la fatturazione nella console Cloud per utilizzare le risorse Google Cloud.
Completare questo codelab non dovrebbe costare molto, se non nulla. Assicurati di seguire le istruzioni riportate nella sezione "Pulizia", che ti consiglia come arrestare le risorse in modo da non incorrere in addebiti oltre questo tutorial. I nuovi utenti di Google Cloud possono usufruire del programma di prova senza costi di 300$.
Attiva Cloud Shell
- Nella console Cloud, fai clic su Attiva Cloud Shell
.
Se non hai mai avviato Cloud Shell, viene visualizzata una schermata intermedia (sotto la piega) che ne descrive le funzionalità. In questo caso, fai clic su Continua e non comparirà più. Ecco come si presenta la schermata intermedia:
Bastano pochi istanti per eseguire il provisioning e connettersi a Cloud Shell.
Questa macchina virtuale è dotata di tutti gli strumenti di sviluppo di cui avrai bisogno. Offre una home directory permanente da 5 GB e viene eseguita in Google Cloud, migliorando notevolmente le prestazioni e l'autenticazione della rete. Gran parte del lavoro per questo codelab, se non tutto, può essere svolto semplicemente con un browser o un 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>
Se non è così, puoi impostarlo con questo comando:
gcloud config set project <PROJECT_ID>
Output comando
Updated property [core/project].
3. Crea un database
Dopo l'avvio 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'archetipo di avvio rapido di Maven per creare una semplice applicazione 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 nella 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 scrivere un'applicazione Java che utilizzi Hibernate per comunicare con Cloud Spanner. A questo scopo, avremo bisogno di Cloud Spanner Dialect for Hibernate, del driver JDBC Cloud Spanner e del core di 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
Dopodiché, creeremo i file di configurazione di Hibernate hibernate.cfg.xml e hibernate.properties. Esegui il comando seguente per creare i file vuoti e poi 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 il file hibernate.cfg.xml. Creeremo le classi di entità in un secondo momento.
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 anche sapere come connettersi all'istanza Cloud Spanner e quale dialetto utilizzare. Quindi, gli diremo di utilizzare SpannerDialect per la sintassi SQL, il driver JDBC Spanner e la stringa di connessione JDBC con le coordinate del database. Queste informazioni vanno inserite 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 recuperare eseguendo il seguente comando:
gcloud config get-value project
Poiché non abbiamo uno schema di database esistente, 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, dovresti anche assicurarti che le credenziali di autenticazione siano configurate 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é stiamo eseguendo l'operazione in Cloud Shell, le credenziali predefinite del progetto sono già configurate.
7. Crea classi di entità annotate
Ora siamo pronti per scrivere del codice.
Definiremo due POJO (Plain Old Java Object) che verranno mappati alle tabelle in Cloud Spanner: Singer e Album. Album avrà una relazione @ManyToOne con Singer. Avremmo potuto anche mappare i Singer alle liste 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 di classe.
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 stiamo utilizzando un UUID generato automaticamente per la chiave primaria. Questo è un tipo di ID preferito in Cloud Spanner, perché evita gli hotspot poiché il sistema divide i dati tra i server in base agli intervalli di chiavi. Funzionerebbe anche una chiave intera che aumenta monotonicamente, ma può essere meno efficiente.
8. Salva ed esegui query sulle entità
Con tutto configurato e gli oggetti entità definiti, possiamo iniziare a scrivere nel database ed eseguire query. Apriremo una Session di Hibernate e la utilizzeremo per eliminare prima tutte le righe della tabella nel metodo clearData(), salvare alcune entità nel metodo writeData() ed eseguire alcune query utilizzando il linguaggio di query di Hibernate (HQL) nel metodo readData().
Sostituisci i contenuti di App.java con i seguenti:
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"<;, &qu>ot;Shaw", makeDate("1998-05-02")));
sess>ion.getTransaction().commit();
}
private static void re<adDat>a(Session session) {
ListSinger singers = session.createQuery("from Singer where birthDate = '1990-01-01' order by lastName")
.list();
ListAlbum 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 relativi alla pulizia dei thread daemon che Maven tenterà di eseguire.
mvn compile exec:java -Dexec.mainClass=codelab.App -Dexec.cleanupDaemonThreads=false
Nell'output dovresti vedere qualcosa di simile a:
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 delle tabelle Singer e Album nel database, vedrai qualcosa di simile a:


9. Libera spazio
Eliminiamo l'istanza Cloud Spanner che abbiamo creato all'inizio per assicurarci che non utilizzi le risorse inutilmente.
gcloud spanner instances delete codelab-instance
10. Complimenti
Complimenti, hai creato correttamente 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 utilizzare Hibernate
- Hai creato due entità: Artist e Album
- Hai generato automaticamente lo schema di database per la tua applicazione
- Hai salvato correttamente le entità in Cloud Spanner e hai eseguito query su di esse
Ora conosci i passaggi chiave necessari per scrivere un'applicazione Hibernate con Cloud Spanner.