Mapping hibernate avec @Formula

Il arrive (parfois) que l’on souhaite dans nos beans mapper un attribut, non pas directement avec une colonne de notre base de données ni même avec des colonnes héritées ni de des jointures. C’est le cas par exemple dans une situation ou l’on souhaiterait par exemple associé à notre objet la valeur moyenne d’un paramètre sur une série de mesure.

Prenons le cas dans un système commercial d’un objet client, qui dispose de données statiques (nom, prénom) mais aussi de données dynamiques “métier” (panier moyen, nombre d’achats réalisés dans les 12 derniers mois).

Soit on fait le choix de mapper ces données directement sur des colonnes, et à chaque achat, en plus d’insérer une nouvelle entrée dans la table achat il faudra mettre à jour ces 2 colonnes de la table client. C’est à proscrire car on va potentiellement lancer des traitements lourds sur un objet qui ne participe pas au workflow d’achat (risque de lenteur, d’erreur…)
C’est pour cela que je recommande le traitement au niveau de l’objet client et pour cela s’appuyer dynamiquement sur les entrées d’achats pour calculer à la volée.

A noter pour les puristes à la recherche de la beauté et/ou de la complexité et/ou de la performance. Le fetching n’est pas lazy. Ce qui peut avoir de l’impact sur les performances si on commence à tirer des grappes d’objets contenants des formulas compliqués.
Sachez qu’il est possible de forcer le lazy.loading par l’annotation @Basic(fetch = FetchType.LAZY) et en forçant le bytecode.
Je ne détaille pas ceci aux puristes ce seraient leur faire injure. Dans ce cas le DTO (data transfert object) à base de projections est l’objet le plus indiqué.

@Table(name = "client")
@Entity
public class Client {

        private long idTechnique;
	private String nom;
	private String prenom;
        // c'est ça qu'on veut calculer depuis la table vente
	private float panierMoyen;
        private int nbAchats;
}

et une table des ventes

@Table(name = "vente")
@Entity
public class Vente {

        // l'id du client acheteur
        private long acheteurId;
	private float valeur;
	private Date date;
}

les mappings par annotation des champs autres que panierMoyen et nbAchats sont triviaux et on retrouvera des entrées du style

        // useless mais on le met pour l'exemple
        @Column(name = "nom")
	public String getNom() {
		return nom;
	}

	public void setNom(String nom) {
		this.nom = nom;
	}

pour nbAchats on calculera sur le nombre d’entrée pour notre client sur les 12 derniers mois soit une requete de cette forme (en fonction des SGBD)
query = select count(*) from vente where acheteurId=idTechnique and date > CURDATE() – INTERVAL 12 MONTH

et donc un mapping

        @Formula("select count(*) from vente where acheteurId=idTechnique and date > CURDATE() - INTERVAL 12 MONTH ")
	public String getPanierMoyen() {
		return panierMoyen;
	}

Je laisse aux lecteurs l’utilisation de la fonction de moyenne ( avg() ) pour calculer et mapper le panier moyen. Je donnerai la réponse si certains n’y arrivaient pas.

Bons mappings!

About Matthieu

Hello, I joined Java-Hoster in 2009. After my engineering studies in France, I worked for several companies including some well known Natixis , Airbus and Air France . Now I am more focused and involved on Java open-source projects and of course in Java-Hoster which makes me learn a lot and feed me as well :-D I am also responsible for this blog, so if you have something to say about it feel free to contact me. Cheers.
This entry was posted in Database, hibernate and tagged , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>