2009-11-12 12 views
6

J'ai une instance d'une classe que j'ai obtenue à partir d'une session Hibernate. Cette session est révolue depuis longtemps. Maintenant, j'appelle toString() et j'obtiens le LazyInitializationException: could not initialize proxy - no Session attendu puisque j'essaye d'accéder à une référence qu'Hibernate n'a pas résolue pendant le chargement de l'instance (chargement paresseux).Comment implémenter toString() dans une classe mappée avec Hibernate?

Je ne veux pas vraiment faire le chargement désireux puisqu'il changerait la requête d'environ 120 caractères à plus de 4KB (avec huit jointures). Et je n'ai pas à: Tout ce que je veux afficher dans toString() est l'ID de l'objet référencé; c'est-à-dire quelque chose qu'Hibernate doit savoir à ce moment (ou il ne peut pas faire le chargement paresseux).

Donc, ma question: Comment gérez-vous ce cas? N'essayez jamais d'utiliser des références dans toString()? Ou appelez-vous toString() dans le code de chargement juste au cas où? Ou y at-il une fonction d'utilité dans Hibernate qui va retourner quelque chose d'utile quand je lui passe une référence qui pourrait être être paresseux? Ou évitez-vous les références au toString()?

+0

Si Java a la fermeture, vous pouvez faire: String x = lazyToString ({=> this.getY()}) + lazyToString ({=> this.getZ()}); et attraper l'expection dans la méthode lazyToString. L'overhead avec classes internes (ou try/catch) est trop élevé pour le faire. –

+0

Oui, mais ça ne me donnerait pas une séance non plus. –

+0

C'est vrai. Vous pouvez seulement imprimer que la valeur n'est pas chargée. Je pensais que c'était l'intention. Vous ne pourrez pas démarrer une session et associer l'objet à l'appel de la méthode toString. –

Répondre

5

Il est possible de le faire en définissant le type d'accès du champ ID sur "property". comme:

@Entity 
public class Foo { 
    // the id field is set to be property accessed 
    @Id @GeneratedValue @AccessType("property") 
    private long id; 
    // other fields can use the field access type 
    @Column private String stuff; 
    public long getId() { return id; } 
    public void setId(long id) { this.id = id; } 
    String getStuff() { return stuff; } 
    // NOTE: we don't need a setStuff method 
} 

Il est expliqué here. Ainsi, le champ id est toujours rempli lorsqu'un proxy est créé.

+0

+1 J'aime ça; il y a juste une petite erreur: j'utilise la syntaxe DSL, donc mon getter s'appelle "id()", pas "getId()". Je suppose que je pourrais ajouter un second getter pour ce cas particulier mais peut-être est-il possible de dire à Hibernate le nom du getter? –

+0

Eh bien, c'est possible en créant votre propre implémentation de org.hibernate.property.PropertyAccessor, et déclarez le nom qualifié complet comme valeur pour @AccessType. D'un autre côté, vous pouvez créer le setter (vous en aurez besoin aussi) et getter et les rendre privés, de sorte que vous ne les verrez pas du reste de votre application. – EJB

+0

@EJB: Cela a-t-il vraiment fonctionné? J'ai une situation où la classe A -----> (a un à plusieurs rel) avec la classe B. A et B ont plusieurs propriétés. Donc, quand je fais un appel à la méthode toString() pour la classe A, il échoue avec l'exception LazyInitialization (comme ci-dessus) malgré la définition du @AccessType ("property") pour le champ Id de la classe A. – dirai

0

Si tout ce que vous voulez retourner est l'ID de l'objet, j'imagine appeler getID(), puis analyser la int/long comme une valeur de chaîne au moment où vous voulez qu'il fonctionne très bien. Au moins, c'est comme ça que ça se base sur la question.

EDIT

How to solve the LazyInitializationException using JPA and Hibernate

Après avoir visionné le commentaire et de faire quelques recherches je crois que cela peut être plus bénéfique pour votre scénario.

+0

Cela lancera l'exception LazyInitializationException parce que la référence n'a pas encore été résolue. –

+0

Aaron, après avoir lu ce commentaire, j'ai édité mon post. S'il vous plaît voir les nouvelles informations et laissez-moi savoir si cela résout le problème. – Woot4Moo

+0

@Woot - Les réponses ne vont pas aider. Les valeurs initialisées paresseuses ne seront jamais lues. Le tx est commis. La connexion est fermée –

1

J'ai trouvé une solution de contournement:

public static String getId (DBObject dbo) 
{ 
    if (dbo == null) 
     return "null"; 

    if (dbo instanceof HibernateProxy) 
    { 
     HibernateProxy proxy = (HibernateProxy)dbo; 
     LazyInitializer li = proxy.getHibernateLazyInitializer(); 
     return li.getIdentifier().toString(); 
    } 

    try 
    { 
     return Long.toString (dbo.id()); 
    } 
    catch (RuntimeException e) 
    { 
     return "???"; 
    } 
} 

Alors, que ce code est fait, il va chercher l'ID (un numéro de 64 bits) de l'objet. DBObject est une interface qui définit long id(). Si l'objet est un proxy Hibernate, alors je parle à son LazyInitializer pour obtenir l'ID. Sinon, j'appelle le id(). Utilisation:

class Parent { 
    DBObject child; 
    public String toString() { 
     return "Parent (id=..., child=" + getId(child)+")"); 
    } 
} 
0

J'ai trouvé que la meilleure solution pour les bonnes pratiques est une modification de la solution trouvée sur ce blog: http://www.nonfunc.com/2016/02/05/jpa-performance-gotcha-tostring-really/. Il a nécessité une modification pour les champs Nullable et pour les objets Collections.

public toString() { 
    return String.format("FuBar [id=%d" + 
     + ", fu=%s" // fu is a lazy non-nullable field 
     + ", bar=%s" // bar is a nullable lazy field 
     + ", borks=%s]", // borks is a lazy collection of Bork objects 
     id, 
     fu instanceof HibernateProxy ? "[null]" : bar.toString(), 
     bar == null || bar instanceof HibernateProxy ? "[null]" : bar.toString(), 
     borks instanceof PersistentSet ? "[null]" : borks.toString()); 
} 
+1

Je vois ce que vous faites là . Quelques commentaires: Mélanger 'String.format()' et '+' est mauvais pour la performance. Vous devriez également remplacer '[null]' par '' parce que le proxy n'est pas nul - il n'est tout simplement pas chargé. Une approche encore meilleure serait de retourner '<' où 'TYPE' est le type d'entité et' ID' est la clé primaire. –

+0

Code modifié pour les performances. L'exemple n'était pas exactement ce que j'ai et la concaténation d'une variable et d'une chaîne affecte les performances (la chaîne de format devrait être compilée dans une chaîne statique), mais je pense que transpirer sur les performances de toString n'est pas très important, car il serait utilisé pour le débogage ou le rapport d'erreurs et ne devrait pas être appelé très fréquemment dans les situations de production. J'aime la suggestion de spécifier paresseux vs null, cependant. – Nielsvh