1. Présentation
Hibernate est devenu la solution ORM standard de facto pour les projets Java. Cette solution est compatible avec toutes les principales bases de données relationnelles et permet d'utiliser des outils ORM encore plus puissants tels que Spring Data JPA. Il existe également de nombreux frameworks compatibles avec Hibernate, tels que Spring Boot, Microprofile et Quarkus.
Le dialecte Cloud Spanner pour Hibernate ORM permet d'utiliser Hibernate avec Cloud Spanner. Vous bénéficiez des avantages de Cloud Spanner (évolutivité et sémantique relationnelle) avec la persistance idiomatique de Hibernate. Vous pouvez ainsi migrer vos applications existantes vers le cloud ou en créer de nouvelles en profitant d'une productivité accrue des développeurs offerte par les technologies basées sur Hibernate.
Points abordés
- Écrire une application Hibernate simple qui se connecte à Cloud Spanner
- Créer une base de données Cloud Spanner
- Utiliser le dialecte Cloud Spanner pour Hibernate ORM
- Comment implémenter des opérations CRUD (create-read-update-delete) avec Hibernate
Prérequis
2. Préparation
Configuration de l'environnement d'auto-formation
- Connectez-vous à Cloud Console, puis créez un projet ou réutilisez un projet existant. (Si vous n'avez pas encore de compte Gmail ou G Suite, vous devez en créer un.)
Mémorisez l'ID du projet. Il s'agit d'un nom unique permettant de différencier chaque projet Google Cloud (le nom ci-dessus est déjà pris ; vous devez en trouver un autre). Il sera désigné par le nom PROJECT_ID
tout au long de cet atelier de programmation.
- Vous devez ensuite activer la facturation dans Cloud Console pour pouvoir utiliser les ressources Google Cloud.
L'exécution de cet atelier de programmation est très peu coûteuse, voire gratuite. Veillez à suivre les instructions de la section "Nettoyer" qui indique comment désactiver les ressources afin d'éviter les frais une fois ce tutoriel terminé. Les nouveaux utilisateurs de Google Cloud peuvent participer au programme d'essai sans frais pour bénéficier d'un crédit de 300 $.
Activer Cloud Shell
- Dans Cloud Console, cliquez sur Activer Cloud Shell .
Si vous n'avez encore jamais démarré Cloud Shell, un écran intermédiaire s'affiche en dessous de la ligne de séparation pour décrire de quoi il s'agit. Si tel est le cas, cliquez sur Continuer (cet écran ne s'affiche qu'une seule fois). Voici à quoi il ressemble :
Le provisionnement et la connexion à Cloud Shell ne devraient pas prendre plus de quelques minutes.
Cette machine virtuelle contient tous les outils de développement nécessaires. Elle intègre un répertoire d'accueil persistant de 5 Go et s'exécute sur Google Cloud, ce qui améliore nettement les performances réseau et l'authentification. Vous pouvez réaliser une grande partie, voire la totalité, des activités de cet atelier dans un simple navigateur ou sur votre Chromebook.
Une fois connecté à Cloud Shell, vous êtes en principe authentifié et le projet est défini avec votre ID de projet.
- Exécutez la commande suivante dans Cloud Shell pour vérifier que vous êtes authentifié :
gcloud auth list
Résultat de la commande
Credentialed Accounts ACTIVE ACCOUNT * <my_account>@<my_domain.com> To set the active account, run: $ gcloud config set account `ACCOUNT`
gcloud config list project
Résultat de la commande
[core] project = <PROJECT_ID>
Si vous obtenez un résultat différent, exécutez cette commande :
gcloud config set project <PROJECT_ID>
Résultat de la commande
Updated property [core/project].
3. Créer une base de données
Après le lancement de Cloud Shell, vous pouvez commencer à utiliser gcloud
pour interagir avec votre projet GCP.
Commencez par activer l'API Cloud Spanner.
gcloud services enable spanner.googleapis.com
Créons maintenant une instance Cloud Spanner appelée codelab-instance
.
gcloud spanner instances create codelab-instance \ --config=regional-us-central1 \ --description="Codelab Instance" --nodes=1
Nous devons maintenant ajouter une base de données à cette instance. Nous l'appellerons codelab-db
.
gcloud spanner databases create codelab-db --instance=codelab-instance
4. Créer une application vide
Nous allons utiliser l'archetype de démarrage rapide Maven pour créer une application de console Java simple.
mvn archetype:generate \ -DgroupId=codelab \ -DartifactId=spanner-hibernate-codelab \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DarchetypeVersion=1.4 \ -DinteractiveMode=false
Accédez au répertoire de l'application.
cd spanner-hibernate-codelab
Compilez et exécutez l'application à l'aide de Maven.
mvn compile exec:java -Dexec.mainClass=codelab.App
Hello World!
doit s'afficher dans la console.
5. Ajouter des dépendances
Découvrons le code source en ouvrant l'éditeur Cloud Shell et en parcourant le répertoire spanner-hibernate-codelab
.
Jusqu'à présent, nous n'avons qu'une application de base de la console Java qui affiche "Hello World!"
. Cependant, nous voulons vraiment écrire une application Java qui utilise Hibernate pour communiquer avec Cloud Spanner. Pour cela, nous avons besoin du dialecte Cloud Spanner pour Hibernate, du pilote JDBC Cloud Spanner et du cœur Hibernate. Ajoutons donc les dépendances suivantes au bloc <dependencies>
dans le fichier 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. Configurer Hibernate ORM
Nous allons ensuite créer les fichiers de configuration Hibernate hibernate.cfg.xml
et hibernate.properties
. Exécutez la commande suivante pour créer les fichiers vides, puis modifiez-les à l'aide de l'éditeur Cloud Shell.
mkdir src/main/resources \ && touch src/main/resources/hibernate.cfg.xml \ && touch src/main/resources/hibernate.properties
Nous allons donc indiquer à Hibernate les classes d'entités annotées que nous allons mapper à la base de données en renseignant hibernate.cfg.xml
. (Nous créerons les classes d'entités ultérieurement.)
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 doit également savoir comment se connecter à l'instance Cloud Spanner et choisir le dialecte à utiliser. Nous allons donc lui demander d'utiliser SpannerDialect
pour la syntaxe SQL, le pilote JDBC Spanner et la chaîne de connexion JDBC avec les coordonnées de la base de données. Ce code est placé dans le fichier 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
N'oubliez pas de remplacer {PROJECT_ID}
par l'ID de votre projet, que vous pouvez obtenir en exécutant la commande suivante:
gcloud config get-value project
Comme nous ne disposons d'aucun schéma de base de données, nous avons ajouté la propriété hibernate.hbm2ddl.auto=update
pour permettre à Hibernate de créer les deux tables dans Cloud Spanner lorsque nous exécutons l'application pour la première fois.
En règle générale, vous devez également vous assurer que les identifiants d'authentification sont configurés à l'aide d'un fichier JSON de compte de service dans la variable d'environnement GOOGLE_APPLICATION_CREDENTIALS
ou des identifiants par défaut de l'application configurés à l'aide de la commande gcloud auth application-default login
. Toutefois, comme nous s'exécutons dans Cloud Shell, les identifiants de projet par défaut sont déjà configurés.
7. Créer des classes d'entités annotées
Nous sommes maintenant prêts à écrire du code.
Nous allons définir deux anciens objets Java (POJO) standards qui seront mappés à des tables dans Cloud Spanner : Singer
et Album
. Album
aura une relation @ManyToOne
avec Singer
. Nous aurions également pu mapper des Singer
à des listes de leurs Album
avec une annotation @OneToMany
, mais pour cet exemple, nous ne voulons pas vraiment charger tous les albums chaque fois que nous avons besoin d'extraire un chanteur de la base de données.
Ajoutez les classes d'entités Singer
et Album
.
Créez les fichiers du cours.
touch src/main/java/codelab/Singer.java \ && touch src/main/java/codelab/Album.java
Collez le contenu des fichiers.
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;
}
}
Notez que dans cet exemple, nous utilisons un UUID généré automatiquement pour la clé primaire. Il s'agit d'un type d'ID privilégié dans Cloud Spanner, car il évite les hotspots, car le système divise les données entre les serveurs par plages de clés. Une clé entière augmentant de manière monotone fonctionne également, mais peut être moins performante.
8. Enregistrer et interroger des entités
Maintenant que tout est configuré et que les objets d'entité sont définis, nous pouvons commencer à écrire dans la base de données et à l'interroger. Nous allons ouvrir un Session
Hibernate, puis l'utiliser pour supprimer d'abord toutes les lignes de la table dans la méthode clearData()
, enregistrer certaines entités dans la méthode writeData()
et exécuter des requêtes à l'aide du langage de requête Hibernate (HQL) dans la méthode readData()
.
Remplacez le contenu de App.java
par le code suivant :
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;
}
}
}
Maintenant, compilons et exécutons le code. Nous allons ajouter l'option -Dexec.cleanupDaemonThreads=false
pour supprimer les avertissements concernant le nettoyage des threads daemon que Maven tentera de réaliser.
mvn compile exec:java -Dexec.mainClass=codelab.App -Dexec.cleanupDaemonThreads=false
Le résultat doit ressembler à ceci:
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
À ce stade, si vous accédez à la console Cloud Spanner et affichez les données des tables "Singer" et "Album" dans la base de données, vous obtenez un résultat semblable à celui-ci:
9. Effectuer un nettoyage
Supprimons l'instance Cloud Spanner que nous avons créée au début pour nous assurer qu'elle n'utilise pas inutilement de ressources.
gcloud spanner instances delete codelab-instance
10. Félicitations
Félicitations ! Vous venez de créer une application Java qui utilise Hibernate pour conserver des données dans Cloud Spanner.
- Vous avez créé une instance et une base de données Cloud Spanner
- Vous avez configuré l'application pour utiliser Hibernate.
- Vous avez créé deux entités : "Artiste" et "Album".
- Vous avez automatiquement généré le schéma de base de données de votre application
- Vous avez enregistré des entités dans Cloud Spanner et vous les avez interrogées
Vous connaissez maintenant les principales étapes à suivre pour écrire une application Hibernate avec Cloud Spanner.