Sommaire
Sommaire
<<

Hibernate : les annotations  il y a 4 années

Auteur

Hachim IDRISSI YASSINE, de ProfLibre.

Série

HIBERNATE : l’essentiel

Description

L’objectif de cette formation est de savoir comment créer une entité Hibernate avec les annotations.

Tags

Introduction

L’objectif de cette formation est de savoir comment créer une entité Hibernate avec les annotations.

I. Prérequis

Ayant consulté les deux formations précédentes dans la même série : https://www.proflibre.com/?serie=55

[Hibernate] Vue d’ensemble

https://www.proflibre.com/JAVA_:_HIBERNATE/[Hibernate]_Vue_d%E2%80%99ensemble/1

Hibernate Native : configurer

https://www.proflibre.com/JAVA_:_HIBERNATE/Hibernate_Native_:_configurer/2

II. Diagramme de classes

Diagramme de classes « UML » :

PersonneAnnot

- id : Long

- nom : String

- prenom : String

- biographie : String

- age : int

- poids : Double

- compte : BigDecimal

- dateMariage : Date

- dateInsertion : Timestamp

+ Getters & Setters

III. La classe JAVA initiale « PersonneAnnot.java »

Voici la classe PersonneAnnot.java initiale sans les annotations :

package com.proflibre.annotations;

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.Date;

public class PersonneAnnot {
	
	private Long id;
	private String nom;
	private String prenom;
	private String biographie;
	private int age;
	private Double poids;
	private BigDecimal compte;
	private Date dateMariage;
	private Timestamp dateInsertion;
	
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getNom() {
		return nom;
	}
	public void setNom(String nom) {
		this.nom = nom;
	}
	public String getPrenom() {
		return prenom;
	}
	public void setPrenom(String prenom) {
		this.prenom = prenom;
	}
	public String getBiographie() {
		return biographie;
	}
	public void setBiographie(String biographie) {
		this.biographie = biographie;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public Double getPoids() {
		return poids;
	}
	public void setPoids(Double poids) {
		this.poids = poids;
	}
	public BigDecimal getCompte() {
		return compte;
	}
	public void setCompte(BigDecimal compte) {
		this.compte = compte;
	}
	public Date getDateMariage() {
		return dateMariage;
	}
	public void setDateMariage(Date dateMariage) {
		this.dateMariage = dateMariage;
	}
	public Timestamp getDateInsertion() {
		return dateInsertion;
	}
	public void setDateInsertion(Timestamp dateInsertion) {
		this.dateInsertion = dateInsertion;
	}
	
}

 

IV. Schéma relationnel

Schéma relationnel « MLD / MySQL » de la table « PERSONNEANNOT » qu’on souhaite générer automatiquement avec Hibernate :

Schéma relationnel

V. Les annotations

Une annotation est une forme de métadonnées qui fournissent des données sur un programme qui ne fait pas partie du programme initial lui-même.

Source : https://docs.oracle.com/javase/tutorial/java/annotations/

Les annotations n'ont aucun impact direct sur le fonctionnement du code qu'elles annotent, mais elles le complètent pour qu’il soit reconnu, compréhensible et compatible avec un autre programme, un standard ou un framework.

Les annotations sont utilisées avec tous les types d'entités et de membres de Java : packages, classes, interfaces, constructeurs, méthodes, champs, paramètres, variables ou annotations elles-mêmes.

Syntaxiquement, elles commencent par le symbole @.

VI. Choisir le typage

Pour faire la correspondance entre les types Hibernate, JAVA et SGBD (MySQL), rendez-vous sur la documentation officielle (rubrique 2.3. Basic Types) :

https://docs.jboss.org/hibernate/orm/5.3/userguide/html_single/Hibernate_User_Guide.html#basic

Trois déclarations possibles :

1. L’annotation “@Type” du package “org.hibernate.annotations.Type” :

@Type(type = "org.hibernate.type.StringType")

L’attribut type peut prendre comme valeur le :

  • Nom d’un type Hibernate : Hibernate type (org.hibernate.type package), exemple : org.hibernate.type.StringType
  • Nom d'une classe Java : Java type, exemple : java.lang.String
  • Nom d'un type basique Hibernate : BasicTypeRegistry key(s), exemple : string.

2. L’attribut “columnDefinition” de l’annotation “@javax.persistence.Column” :

@Column(name = "date_insertion", columnDefinition = "TIMESTAMP")

L’attribut columnDefinition permet de spécifier la défintion du champ dans la base de données (LDD), il peut donc prendre comme valeur le type du champ dans le SGBD, ce qui correspond à la colonne JDBC type de la documentation officielle, exemple : VARCHAR.

Pour spécifier la taille d’une chaîne de caractères, il suffit de rajouter l’attribut length à l’annotation @Column, exemple : @Column(name="nom",length=500)

3. La déclaration des types “Date” et “Calendar” avec “@javax.persistence.Temporal” :

@Temporal(TemporalType.DATE)

Tout comme java.util.Date, java.util.Calendar nécessite l'annotation @Temporal

afin de savoir quel type de données JDBC choisir: DATE, TIME ou TIMESTAMP.

Comme le mappage entre les classes de date / heure Java 8 et les types SQL est implicite, il n'est pas nécessaire de spécifier l'annotation @Temporal. La définir sur les classes java.time ou java.sql.Timestamp par exemple lève l'exception suivante:

org.hibernate.AnnotationException: @Temporal should only be set on a java.util.Date or java.util.Calendar property

NB : si java.util.Date marque un point dans le temps, java.util.Calendar prend en compte le fuseau horaire par défaut.

VII. Remplir la fiche préparatrice

Propriétés JAVA/Hibernate

Types JAVA

Types Hibernate

Champs MySQL

Types MySQL

id

Long

long

personne_id

bigint

nom

String

org.hibernate.type.StringType

nom

varchar

prenom

String

java.lang.String

prenom

varchar

biographie

String

text

biographie

longtext

age

int

int

age

int

poids

Double

double

poids

double

compte

BigDecimal

big_decimal

compte

decimal

dateMariage

Date

date

dateMariage

date

dateInsertion

Timestamp

timestamp

date_insertion

timestamp

VIII. La clé primaire

1. Spécifier une clé primaire manuellement (assigned) :

@Id
private Long id;

2. Générer une clé primaire automatiquement (spécifier une stratégie de génération) :

Utiliser l’annotation @GeneratedValue  avec @Id :

L'annotation @GeneratedValue spécifie que la valeur d'identificateur d'entité est automatiquement générée à l'aide d'une colonne d'identité, d'une séquence de base de données ou d'un générateur de table. Hibernate prend en charge le mappage @GeneratedValue même pour les identificateurs UUID. Il existe quatre valeurs possibles pour l’attribut strategy sur l'annotation @GeneratedValue : IDENTITY, AUTO (= incrément, par défaut), TABLE et SEQUENCE.

2.1 La stratégie « AUTO » :

La valeur par défaut de @GeneratedValue est « AUTO » du package javax.persistence.GenerationType.AUTO

2.1.1 AUTO sans la stratégie UUID :

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

Ou simplement sans renseigner l’attribut strategy :

@Id
@GeneratedValue
private Long id;

 

Hibernate/MySQL : AUTO sans la stratégie UUID

Hibernate/MySQL : AUTO sans la stratégie UUID

 

2.1.2 AUTO avec la stratégie UUID : utilisation implicite de la stratégie aléatoire UUID.

@Id
@GeneratedValue
private UUID id;

Pour avoir plus d’information sur la stratégie UUID, consultez le lien suivant :

 https://fr.wikipedia.org/wiki/Universal_Unique_Identifier

Hibernate/MySQL : AUTO avec la stratégie UUID

Hibernate/MySQL : AUTO avec la stratégie UUID

 

2.2 La stratégie « IDENTITY » :

IDENTITY représente AUTO_INCREMENT pour le SGBD MySQL :

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)

 

Hibernate/MySQL : la stratégie “IDENTITY”

Hibernate/MySQL : la stratégie “IDENTITY”

Avantage :

Le SGBD génère automatiquement la clé primaire.

Inconvénient :

Hibernate désactive le traitement batch en cas d’usage de la stratégie  «IDENTITY» :

L’insertion sur la base se fait ligne par ligne au lieu d’insertion massive d’un seul coup.

2.3 La stratégie « SEQUENCE » :

2.3.1 Séquence sans nom :

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)

Hibernate/MySQL : la stratégie “SEQUENCE” sans nom

 Hibernate/MySQL : la stratégie “SEQUENCE” sans nom

2.3.2 Séquence nommée :

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "generateur-sequence")
@SequenceGenerator(name = "generateur-sequence", sequenceName = "ma_sequence")

Hibernate/MySQL : la stratégie “SEQUENCE” nommée

Hibernate/MySQL : la stratégie “SEQUENCE” nommée

2.3.2 Séquence configurée :

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "generateur-sequence")
@SequenceGenerator(name = "generateur-sequence", sequenceName = "ma_sequence", allocationSize = 5)

Hibernate/MySQL : la stratégie “SEQUENCE” configurée

Hibernate/MySQL : la stratégie “SEQUENCE” configurée

2.4 La stratégie « TABLE » :

2.4.1 Générateur de table sans nom :

@Id
@GeneratedValue(strategy = GenerationType.TABLE)

Hibernate/MySQL : la stratégie “TABLE” sans nom

Hibernate/MySQL : la stratégie “TABLE” sans nom

2.4.2 Générateur de table configuré :

@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "generateur-table")
@TableGenerator(
name =  "generateur-table",
table = "table_didentification",
pkColumnName = "nom_table",
valueColumnName =  "mon_identifiant",
allocationSize = 5
)

 

Hibernate/MySQL : la stratégie “TABLE” configurée

Hibernate/MySQL : la stratégie “TABLE” configurée

3. Utilisation avancée d’une stratégie Hibernate prédéfinie ou un nom personnalisé de classe via l’attribut strategy de @GenericGenerator :

Utiliser l’annotation @org.hibernate.annotations.GenericGenerator

avec @javax.persistence.GeneratedValue pour utiliser une stratégie Hibernate prédéfinie ou un nom personnalisé (l’attribut name) à condition qu’il soit unique.

Exemple simple avec “increment” (pour un usage avancé, consultez la doc officielle):

@Id
@GeneratedValue(strategy = GenerationType.AUTO, generator = "proflibre")
@GenericGenerator(name = "proflibre", strategy = "increment")

Ou simplement sans renseigner l’attribut strategy de @GeneratedValue :

@Id
@GeneratedValue(generator="proflibre")
@GenericGenerator(name="proflibre", strategy = "increment")

Hibernate/MySQL : la stratégie “increment”.

Hibernate/MySQL : la stratégie “increment”.

Voici quelques stratégies Hibernate prédefinies et que l’on peut utiliser comme valeur de l’attribut strategy de l’annotation @GenericGenerator  :

  • assigned
  • Increment (vue dans l’exemple précédent, cette option reste déconseillée dans un environnement clusterisé   conflit de serveurs en cas d’insertion de la même valeur simultanément)
  • sequence
  • hilo
  • identity
  • native (AUTO_INCREMENT pour MySQL)
  • uuid

IX. Catalogue des déclarations

La liste des annotations disponibles sur la documentation officielle d’Hibernate :

https://docs.jboss.org/hibernate/orm/5.3/userguide/html_single/Hibernate_User_Guide.html#annotations

JPA (javax.persistence.*)

API : https://docs.jboss.org/hibernate/jpa/2.2/api/overview-summary.html

Hibernate (org.hibernate.annotations.*)

API : https://docs.jboss.org/hibernate/orm/5.3/javadocs/

Entity

Type

Table

GenericGenerator

Id

..

GeneratedValue

 

Column

 

Temporal…

 

L’annotation JPA « @Column » du package javax.persistence comme exemple :

https://docs.jboss.org/hibernate/jpa/2.2/api/javax/persistence/Column.html

Spécifie la colonne mappée pour une propriété ou un champ persistant. Si aucune annotation de colonne n'est spécifiée, les valeurs par défaut s'appliquent (Par défaut, la propriété ou le nom du champ).

L’annotation @Column

L’annotation Hibernate « Type » du package org.hibernate.annotations

comme exemple :

https://docs.jboss.org/hibernate/orm/5.3/javadocs/

L’annotation @Type

X. Définir la stratégie d’accès

En tant que fournisseur JPA, Hibernate peut introspecter à la fois les attributs d'entité (champs d'instance) ou les accesseurs (propriétés d'instance). Par défaut, le placement de l'annotation @Id donne la stratégie d'accès par défaut. Lorsqu'il est placé sur un champ, Hibernate assumera un accès basé sur le champ. Lorsqu'il est placé sur le getter d'identifiant, Hibernate utilisera un accès basé sur les propriétés.

1. Accès basé sur le champ (Field-based access) :

@Id
private Long id;

 

2. Accès basé sur la propriété/accesseur (Property-based access) :

@Id
public Long getId() {
     return id;
}

 

3. Constat :

Si vous placez @Id sur un champ et @column sur le getter d’une autre propriété, cette dernière ne sera pas prise en compte; la même chose si vous placez @Id sur le getter de l’identifiant et le @column sur un autre champ, cette dernière déclaration ne sera pas prise en compte par Hibernate.

4. Règle :

Soit toutes les déclarations sont placées sur les champs, soit toutes les déclarations sont placées sur les accesseurs des champs (getters). Et c’est l’annotation @Id qui définit la stratégie selon sur quoi elle est placée : champ ou getter.

L. Conseil supplémentaire:

Si vous êtes en mode développement, passez le connection.pool_size à une valeur supérieure à 1, exemple :

<property name="connection.pool_size">10</property>

Cela vous évitera des exceptions liées à la génération de la clé primaire avec SEQUENCE ou TABLE par exemple.

C. DATETIME vs TIMESTAMP ?

Les types DATETIME et TIMESTAMP sont tous les deux utilisés pour les valeurs qui contiennent des parties de date et d'heure. MySQL les récupère et les affiche au format  'YYYY-MM-DD hh:mm:ss' format.

DATETIME est définie dans l’intervalle '1000-01-01 00:00:00’ à '9999-12-31 23:59:59'.

TIMESTAMP est définie dans l’intervalle '1970-01-01 00:00:01' UTC à '2038-01-19 03:14:07' UTC.

MySQL convertit les valeurs TIMESTAMP du fuseau horaire actuel en UTC pour le stockage, et de retour de l'UTC vers le fuseau horaire actuel pour la récupération. (Cela ne se produit pas pour d'autres types tels que DATETIME.) Par défaut, le fuseau horaire actuel pour chaque connexion est l'heure du serveur.

Source : https://dev.mysql.com/doc/refman/8.0/en/datetime.html

  • Créer un champ de type MySQL DATETIME avec Hibernate :
@Column(name = "date_insertion")
@Type(type = "timestamp")
private Timestamp dateInsertion;
  • Créer un champ de type MySQL TIMESTAMP avec Hibernate (ajout de l’attribut columnDefinition) :
@Column(name = "date_insertion", columnDefinition = "TIMESTAMP")
@Type(type = "timestamp")
private Timestamp dateInsertion;

 

D. La classe finale (annotée) « PersonneAnnot.java »

Ici nous avons changé le nom de la classe initiale Personne.java en PersonneAnnot.java, vous pouvez garder le même nom. Ce changement a eu lieu juste pour séparer les deux classes Personne.java créée dans la formation précédente (déclarer une entité Hibernate avec XML) et PersonneAnnot.java créée dans la présente formation (même s’elles sont dans deux packages différents).

package com.proflibre.annotations;

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.hibernate.annotations.Type;

@Entity
@Table(name = "PERSONNEANNOT")
public class PersonneAnnot {
	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "personne_id")
	private Long id;
	@Column(name = "nom", length = 500)
	@Type(type = "org.hibernate.type.StringType")
	private String nom;
	@Column(name = "prenom", length = 300)
	@Type(type = "java.lang.String")
	private String prenom;
	@Column(name = "biographie")
	@Type(type = "text")
	private String biographie;
	@Column(name = "age")
	@Type(type = "int")
	private int age;
	@Column(name = "poids")
	@Type(type = "double")
	private Double poids;
	@Column(name = "compte", precision = 12, scale = 5)
	@Type(type = "big_decimal")
	private BigDecimal compte;
	@Column(name = "dateMariage")
	@Temporal(TemporalType.DATE)
	private Date dateMariage;
	@Column(name = "date_insertion", columnDefinition="TIMESTAMP")
	@Type(type = "timestamp")
	private Timestamp dateInsertion;
	
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getNom() {
		return nom;
	}
	public void setNom(String nom) {
		this.nom = nom;
	}
	public String getPrenom() {
		return prenom;
	}
	public void setPrenom(String prenom) {
		this.prenom = prenom;
	}
	public String getBiographie() {
		return biographie;
	}
	public void setBiographie(String biographie) {
		this.biographie = biographie;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public Double getPoids() {
		return poids;
	}
	public void setPoids(Double poids) {
		this.poids = poids;
	}
	public BigDecimal getCompte() {
		return compte;
	}
	public void setCompte(BigDecimal compte) {
		this.compte = compte;
	}
	public Date getDateMariage() {
		return dateMariage;
	}
	public void setDateMariage(Date dateMariage) {
		this.dateMariage = dateMariage;
	}
	public Timestamp getDateInsertion() {
		return dateInsertion;
	}
	public void setDateInsertion(Timestamp dateInsertion) {
		this.dateInsertion = dateInsertion;
	}
	
}

 

Conclusion

Dans l’étape suivante, nous allons découvrir le cycle de vie d’une entité Hibernate.

  

Prise de notes

Vous seul vous pouvez voir vos notes.

Mes notes

Commentaires

 il y a 4 années
Hachim IDRISSI YASSINE [L'auteur] : N’hésitez pas à poser des questions ou nous faire part de vos remarques.
Vos retours nous intéressent.

Formations similaires

Etes-vous intéressé par nos dernières formations ?

back to top
contactez-nous
Fermer