ViaXoft

Aller au contenu | Aller au menu | Aller à la recherche

samedi 8 mai 2010

Sélection de colonnes dans Hibernate

Les outils d'ORM (Hibernate, Ibatis, ...) sont aujourd'hui à maturité et ne souffrent plus des problèmes de performance des premières versions. Le code SQL généré, ainsi que la transformation en objets, ont été très largement optimisés.

Cependant selon la taille de la grappe d'objet remontée, il peut subsister des problèmes de performance, liés au nombre et à la complexité des objets à créer. Le cas d'utilisation typique sur une application de gestion est l'affichage d'une liste d'objet complexe sur un écran de recherche.

Prenons le cas par exemple d'une liste de commande (que nous simplifions volontairement) : 

public Class Commande {

  private Destination destination;

  private Set<LigneCommande> lignes;

  private Client client;

  ...

}

Une requête Criteria qui permet de remonter la liste des commandes ressemble à ceci :

DetachedCriteria criteria = DetachedCriteria.forClass(Commande.class,"commande")

.createAlias("commande.Destination", "destination", CriteriaSpecification.LEFT_JOIN)

.createAlias("commande.Lignes", "lignes", CriteriaSpecification.LEFT_JOIN)

.createAlias("commande.Client", "client", CriteriaSpecification.INNER_JOIN);

List<Commande> listCommande = getHibernateTemplate().findByCriteria(criteria);

Cette requête remonte donc tous les objets Commande (toutes les colonnes de la table correspondante), ainsi que les objets liés (et donc toutes les colonnes associées). Dans le cas où la grappe d'objets est importante et composée de nombreux objets, le nombre de lignes remontées devient très important et la constitution des objets très couteuse. Rappelons que nous souhaitons simplement afficher une liste avec seulement quelques colonnes... au moins 95% des données remontées ne nous servent donc à rien.

Depuis la version 3 d'Hibernate a été introduite la notion de Projection. Celle-ci va nous permettre non seulement de remonter seulement les colonnes qui nous intéressent mais également une liste d'objets non gérés par Hibernate ; par exemple nous allons pouvoir remonter directement une liste de DTO (data transfert object) en se basant sur le même objet criteria (forClass(Commande.class,"commande")) :

On définit le DTO :

public class DealListDto implements Serializable  {
    private static final long serialVersionUID = 1L;

    private java.lang.String id;
    private String ref;
    private Date date;
    private String nom;
    private String prenom;
    private String destNom;
    ...
}

puis les propriétés que l'on souhaite remonter, ainsi que le bean avec lequel on veut les mapper :

criteria.setProjection(Projections.projectionList()

.add(Property.forName("commande.Id"), "id")

.add(Property.forName("commande.Ref"), "ref")

.add(Property.forName("commande.Date"), "date")

.add(Property.forName("client.Nom"), "nom")

.add(Property.forName("client.Prenom"), "prenom")

.add(Property.forName("destination.nom"), "destNom")

.setResultTransformer( Transformers.aliasToBean(DealListDto.class));

List<DealListDto> listCommande = getHibernateTemplate().findByCriteria(criteria);

Nous obtenons donc une liste de DealListDto, beaucoup plus légers, que nous pouvons  faire remonter directement sur la couche client.

mardi 24 février 2009

Hibernate : mapping d'une colonne en Set

L’utilisation d’une relation many-to many avec hibernate permet de rendre transparent la table d’association.

Dans l'exemple suivant, la table EDITION_LIVRE sera mappée de manière transparente :
Livre Many Many
La mapping de la table LIVRE sera le suivant :


‹set name="editions" table="EDITION_LIVRE"›
  ‹key column="LIVRE_ID" /›
  ‹many-to-many class="Edition" column="EDITION_ID" /›
‹/set›

Et Livre contiendra un Set<Edition>, mais l'objet EditionLivre n’existera pas…

A la vue de ce type de mapping, on peut être intéressé de faire la même chose pour un mapping many to one.
En effet, plutôt que de me retrouver avec un Set d'objet dont nous n'avons besoin que pour de l'affichage, nous préférerions avoir un Set de String. Par exemple, si on prend une relation de ce type :

Livre Set
Et qu’on ne veut pas avoir à mapper l’objet LivreMotcle, il suffit de définir dans le fichier de mapping Livre le Set suivant :


‹set name=”mots" table="LIVRE_MOTCLE"›
  ‹key column="LIVRE_ID" /›
  ‹element column="MOT" type="string"/›
‹/set›

On obtient alors un Set<String> mots dans l'objet Livre.