204 votes

Faire une relation univoque paresseux

Dans cette application, nous développons, nous avons remarqué que d'un point de vue a été particulièrement lente. J'ai dressé le portrait de la vue et a remarqué qu'il y avait une requête exécutée par hibernate qui a pris 10 secondes, même si il y avait seulement deux objets dans la base de données à extraire. Tous OneToMany et ManyToMany relations étaient paresseux donc ce n'était pas le problème. Lors de l'inspection du SQL en cours d'exécution, j'ai remarqué qu'il y avait plus de 80 rejoint dans la requête.

Outre l'inspection de la question, j'ai remarqué que le problème a été causé par la profondeur de la hiérarchie de l' OneToOne et ManyToOne relations entre les classes d'entité. Alors, j'ai pensé, je vais juste faire extraites paresseux, ce qui devrait résoudre le problème. Mais l'annotation soit @OneToOne(fetch=FetchType.LAZY) ou @ManyToOne(fetch=FetchType.LAZY) ne semble pas fonctionner. Soit j'obtiens une exception ou alors ils ne sont pas réellement remplacé par un objet proxy et donc d'être paresseux.

Des idées comment je vais obtenir que cela fonctionne? Notez que je n'utilise pas le persistence.xml à définir les relations ou les détails de la configuration, tout est fait dans du code java.

208voto

ChssPly76 Points 53452

D'abord, quelques précisions pour KLE's réponse:

  1. Sans contrainte (nullable) one-to-one de l'association est la seule qui ne peut pas être mandatées sans instrumentation du bytecode. La raison pour cela est que le propriétaire de l'entité DOIT savoir si le patrimoine de l'association doit contenir un objet proxy ou NULLE et il ne peut pas déterminer qu'en regardant sa base de colonnes du tableau en raison de l'un-à-un, normalement, être mappés sur les mêmes PK, de sorte qu'il a d'être avec impatience extraites de toute façon faire des proxy inutile. Voici une plus détaillée de l' explication.

  2. plusieurs-à-un les associations (et un-à-plusieurs, évidemment) ne souffrent pas de ce problème. Propriétaire entité peut facilement vérifier ses propres FK (et dans le cas d'un-à-plusieurs, vide de collecte de proxy est créé d'abord et rempli sur demande), afin que l'association puisse être paresseux.

  3. Le remplacement de l'un-à-un à un-à-plusieurs est presque jamais une bonne idée. Vous pouvez le remplacer avec de l'unique plusieurs-à-un, mais il y a d'autres (peut-être mieux).

Rob H. a un point, cependant vous ne pouvez pas être en mesure de la mettre en œuvre en fonction de votre modèle (par exemple, si votre one-to-one de l'association est nullable).

Maintenant, autant que la question de départ est:

A) @ManyToOne(fetch=FetchType.LAZY) devrait fonctionner parfaitement. Êtes-vous sûr que c'est de ne pas être écrasé dans la requête elle-même? Il est possible de spécifier join fetch dans les requêtes HQL et / ou de définir explicitement mode de lecture via les API des Critères qui auraient préséance sur la classe d'annotation. Si ce n'est pas le cas et que vous rencontrez toujours des problèmes, veuillez poster vos classes, de la requête et résultant SQL pour plus de la conversation.

B) @OneToOne est plus délicat. Si ce n'est certainement pas les valeurs null, aller avec Rob H. de la suggestion et de le définir en tant que tel:

@OneToOne(optional = false, fetch = FetchType.LAZY)

Sinon, si vous pouvez modifier votre base de données (ajout d'une colonne de clé étrangère pour le propriétaire de la table), le faire et le mapper comme "rejoint":

@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name="other_entity_fk")
public OtherEntity getOther()

et dans OtherEntity:

@OneToOne(mappedBy = "other")
public OwnerEntity getOwner()

Si vous ne pouvez pas le faire (et ne peut pas vivre avec le chargement agressif) instrumentation du bytecode est votre seule option. Je suis d'accord avec CPerkins, cependant - si vous avez 80!!! les jointures en raison avides de OneToOne associations, vous avez plus de problèmes alors :-)

19voto

Kdeveloper Points 7034

Pour obtenir un chargement différé travaillant sur nullable biunivoque mappages que vous devez laisser hiberner faire compiler instrumentation temps et ajouter un `` de la relation biunivoque.

Exemple de cartographie :

Exemple Ant construire extension de fichier (pour faire de l’instrumentation de temps de compilation de mise en veille prolongée) :

11voto

KLE Points 11711

L'idée de base derrière le XToOnes en veille prolongée, c'est qu'ils ne sont pas paresseux dans la plupart des cas.

Une des raisons est que, quand Hibernate ont à décider de mettre un proxy (avec l'id) ou une valeur null,
il a qu'à regarder dans l'autre table de toute façon à la rejoindre. Le coût d'accès à l'autre table dans la base de données est importante, de sorte qu'il pourrait ainsi récupérer les données de la table à ce moment (non-lazy comportement), au lieu de chercher que dans une demande ultérieure qui aurait besoin d'un deuxième accès à la même table.

Édité: pour plus de détails, veuillez vous référer à ChssPly76 's réponse. Celui-ci est moins précis et détaillé, il n'a rien à offrir. Grâce ChssPly76.

8voto

acdcjunior Points 19898

Voici quelque chose qui a travaillé pour moi (sans instrumentation):

Au lieu d'utiliser @OneToOne sur les deux côtés, j'utilise @OneToMany à l'inverse de la partie de la relation (l'un avec des mappedBy). Qui fait de la propriété d'une collection (List dans l'exemple ci-dessous), mais je le traduire en un élément dans la lecture, la rendant transparente pour les clients.

Cette configuration fonctionne paresseusement, qui est, les sélections sont effectuées uniquement lorsqu' getPrevious() ou getNext() sont appelé - et seulement l'un à sélectionner pour chaque appel.

La structure de la table:

CREATE TABLE `TB_ISSUE` (
    `ID`            INT(9) NOT NULL AUTO_INCREMENT,
    `NAME`          VARCHAR(255) NULL,
    `PREVIOUS`      DECIMAL(9,2) NULL
    CONSTRAINT `PK_ISSUE` PRIMARY KEY (`ID`)
);
ALTER TABLE `TB_ISSUE` ADD CONSTRAINT `FK_ISSUE_ISSUE_PREVIOUS`
                 FOREIGN KEY (`PREVIOUS`) REFERENCES `TB_ISSUE` (`ID`);

La classe:

@Entity
@Table(name = "TB_ISSUE") 
public class Issue {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected Integer id;

    @Column
    private String name;

    @OneToOne(fetch=FetchType.LAZY)  // one to one, as expected
    @JoinColumn(name="previous")
    private Issue previous;

    // use @OneToMany instead of @OneToOne to "fake" the lazy loading
    @OneToMany(mappedBy="previous", fetch=FetchType.LAZY)
    // notice the type isnt Issue, but a collection (that will have 0 or 1 items)
    private List<Issue> next;

    public Integer getId() { return id; }
    public String getName() { return name; }

    public Issue getPrevious() { return previous; }
    // in the getter, transform the collection into an Issue for the clients
    public Issue getNext() { return next.isEmpty() ? null : next.get(0); }

}

5voto

Rob H Points 5599

Dans les mappages d’Hibernate XML natifs, vous pouvez accomplir ceci en déclarant un biunivoque mapping avec la contrainte attribut la valeur true. Je ne sais pas ce que l’équivalent d’annotation Hibernate/JPA de c'est-à-dire et une recherche rapide du doc ne fourni aucune réponse, mais j’espère que cela vous donne une piste pour aller.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X