Différence entre Hibernate et JPA

Hibernate/JPA permet de sauvegarder rapidement un objet java dans une base de données. Il permet de s’affranchir des requêtes SQL écrite à la main et le plus souvent difficile à maintenir dans le temps

Modèle de donnée avec JPA/Hibernate

Avec JDBC Classique, nous aurions probablement écrit des requêtes SQL dans une classe UserDao proposant un ensemble de méthode pour gérer les objets User et les relier à une base de données. C’est typiquement ce que proposait cet ancien article sur la réalisation d’un projet Spring.

JPA et Hibernate vont nous permettre de nous affranchir de l’écriture de ces longues requêtes SQL. Il s’agit de ce qu’on appelle des ORM  (object-relational mapping) ou en français Mapping objet-relationnel. Il s’agit d’un Framework qui va se charger de faire la correspondance entre notre objet User et la table correspondante dans la base de données.

Quel est la différence entre Hibernate et JPA?

Hibernate était le framework historique, le plus populaire et il est un des pionners à proposer ce type de solution.

Face à la popularité croissante des ORM, la communauté Java a décider d’éditer un certain nombre de règles généralistes regroupées au sein du standard Java Persistence Api ou JPA. JPA est donc une interface définissant un certain nombre de mots-clés et de normes à respecter.

Les différents solutions/implémentations se sont adapté à ce nouveau standard, pour permettre une meilleur adoption de leurs frameworks. Hibernate est une des implémentations les plus aboutit du standard JPA.

Standard / Implémentation dans le monde Java

Dans le monde Java, il n’est pas rare de retrouver à plusieurs reprises cette notion de standard avec une une plusieurs implémentations de ce même standard. La plupart du temps, le standard est écrit bien après les premières implémentations.

Face au succès des librairies permettant de gérer les logs dans les applications (log4j et d’autres concurrents), la communauté a ensuite dicté un standard Java Logging Api. Les différentes librairies ont ensuite évolué pour se conformer à ce standard.

A quoi ressemble un modèle JPA

L’exemple utilisé dans cet article est la modélisation d’un objet User et les annotations nécessaires pour Hibernate et JPA

package fr.w3blog.jpa.model;

public class User {

	private Integer id;

	private String email;

	private String login;

	private String password;

}

Quel que soit l’implémentation retenu (Hibernate, Toplink), grâce au standard, les annotations à utilisés vont être identiques. Voici un exemple de notre classe User une fois annoté

package fr.w3blog.sprintrest.model;

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.UniqueConstraint;

import javax.validation.constraints.NotNull;

import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;

@Entity//Permet d'indiquer qu'il s'agit d'une classe que l'on souhaite persister
@Table(name="user")//Indique le nom de la table dans la base de données
public class User {

	@Id//Indique qu'il s'agit de la clé primaire
	@GeneratedValue(strategy=GenerationType.AUTO)//Indique comment la clé doit être généré
	@Column(name="id")//Colonne Sql à utiliser
	private Integer id;

	@Column(name="email")
	@NotEmpty//On indique que ca ne doit pas être vide
	@Email(message="Email is invalid")//Indique que l'on souhaite une adresse mail
	private String email;

	@Column(name="pseudo")
	@NotNull
	private String pseudo;

	@Column(name="password")
	private String password;

	/**
	 * @return the id
	 */
	public Integer getId() {
		return id;
	}

	/**
	 * @param id the id to set
	 */
	public void setId(Integer id) {
		this.id = id;
	}

	/**
	 * @return the email
	 */
	public String getEmail() {
		return email;
	}

	/**
	 * @param email the email to set
	 */
	public void setEmail(String email) {
		this.email = email;
	}

	/**
	 * @return the pseudo
	 */
	public String getPseudo() {
		return pseudo;
	}

	/**
	 * @param pseudo the pseudo to set
	 */
	public void setPseudo(String pseudo) {
		this.pseudo = pseudo;
	}

	/**
	 * @return the password
	 */
	public String getPassword() {
		return password;
	}

	/**
	 * @param password the password to set
	 */
	public void setPassword(String password) {
		this.password = password;
	}

}

Comme on le voit içi, on fait référence à des annotations du package javax.persistence dans les premiers imports, il n’est pas mentionné de package Hibernate.

Il serait donc techniquement possible de passer d’une implémentation à l’autres sans avoir à modifier les classes de notre modèle ou presque. Dans le fait, c’est très rare de changer d’implémentation, mais cette standardisation à toute de même le mérite de standardise le nom des annotations. Ainsi, un développeur habitué à hibernate peux facilement comprendre une entité développée pour une autre implémentation

Java Beans Validation

Dans l’exemple ci-dessus, nous avons également ajouté quelques annotations issues de la spécification Bean Validation (JSR-303) qui vont permettre de contrôler le format des différents champs. Il s’agit là aussi d’un standard proposé par Java

L’annotation javax.validation.constraints.NotNull permet de vérifier qu’un champ ne sera pas null avant l’insertion en base de donnée

Toutes ces annotations définies par le standard seront reconnues par Hibernate. Comme évoqué plus haut, Hibernate est le framework le plus utilisé, il possède donc certaines annotations de contrôle des champs qui n’ont pas encore été accepté par le standard. Dans ce cas, il faut faire référence directement à une annotation Hibernate. Il y a dans ce cas de forte chance pour que les autres implémentations ne reconnaissent pas ces annotations non standardisé.