14 septembre 2009

Hibernate est un sacré comique

Alerté par Julien, je fais un peu joujou avec les annotations @OneToOne et Hiernate.
Voici grosso modo ce que je manipule :


@OneToOne( fetch = FetchType.LAZY )
private Foo precedent;

@OneToOne( mappedBy = "precedent", fetch = FetchType.LAZY )
private Foo suivant;
Et là, dès que je charge une instance, j'ai autant de select qui partent en base que j'ai d'éléments dans ma chaîne suivant-précédent...

Le piège, c'est que malgré le LAZY, hibernate veut identifier si la relation OneToOne inverse (mappedBy) est renseignée ou non. En fonction de la réponse il colle un proxy ou null.

La solution, bien que pas très esthétique dans le code est simple : utiliser des OneToMany et se baser sur des collections de 1 élément :


@OneToMany( fetch = FetchType.LAZY )
private Set<Foo> suivants = new HashSet<>();

public Foo getSuivant()
{
Iterator<Foo> it = suivants.iterator();
return it.hasNext() ? it.next() : null;
}

public void setSuivant( Foo suivant )
{
for ( Foo item : suivants )
{
item.setPrecedent( null );
}
this.suivants.clear();

suivant.setPrecedent( this );
suivants.add( suivant );
}


Ca sent la grosse magouille, mais au moins ça ne casse pas la logique des get/set. Comme quoi la persistance "transparente" c'est pas encore tout à fait ça ;)

3 commentaires:

Gabriel a dit…

Il y aurait pas plutôt un fetch=lazy à rajouter? normalement en jpa (contrairement au comportement d'hibernate au départ) les @...toOne font l'objet d'une requête, ils ne sont pas lazy par défaut. C'est pas plutôt ça le problème?

nicolas deloof a dit…

et non, ce serait trop facile :)
C'est une limitation connue d'Hibernate.

Au passage, JPA ne définit pas ce qui doit faire l'objet d'une requête (c'est à l'implémentation de faire sa sauce)

Gabriel a dit…

Ah oui ok! J'avais mal lu/vu. D'accord avec toi, c'est pas beau... Dommage de devoir adapter son implémentation de la couche métier à l'outil de mapping. I want my sql back! (euh... en fait non, mais bon)
Je voulais dire : quand on fait une requête sur un objet, les champs qui sont en relation ...toOne font par défaut l'objet d'une requête en base (fetch eager).