2009-11-12 7 views
6

Ich habe eine Instanz einer Klasse, die ich aus einer Hibernate-Sitzung erhalten habe. Diese Sitzung ist lange vorbei. Jetzt rufe ich toString() an und ich bekomme die erwartete LazyInitializationException: could not initialize proxy - no Session, da ich versuche, auf eine Referenz zuzugreifen, die Hibernate beim Laden der Instanz nicht aufgelöst hat (Lazy Loading).Wie implementiere ich toString() in einer Klasse, die Hibernate zugeordnet ist?

Ich möchte nicht wirklich das Laden eifrig machen, da es die Abfrage von ungefähr 120 Zeichen zu über 4KB (mit acht Joins) ändern würde. Und ich muss nicht: Alles, was ich in toString() anzeigen möchte, ist die ID des referenzierten Objekts; d. h. etwas, das Hibernate zu diesem Zeitpunkt wissen muss (oder das lazy loading nicht ausführen kann).

Also meine Frage: Wie gehst du mit diesem Fall um? Versuchen Sie nie Referenzen in toString() zu verwenden? Oder rufst du toString() im Ladecode an, nur für den Fall? Oder gibt es eine Utility-Funktion in Hibernate, die etwas nützliches zurückgibt, wenn ich eine Referenz übergebe, die könnte faul sein? Oder vermeiden Sie Referenzen in toString() insgesamt?

+0

Wenn Java hatte Verschluss Sie tun könnten: String x = lazyToString ({=> this.getY()}) + lazyToString ({=> this.getZ()}); und fangen Sie die Expect in der LazyToString-Methode. Der Overhead mit inneren Klassen (oder try/catch) ist zu hoch, um dies zu tun. –

+0

Ja, aber das würde mir auch keine Sitzung geben. –

+0

Das stimmt. Sie können dann nur drucken, dass der Wert nicht geladen ist. Ich dachte, das war die Absicht. Sie können keine Sitzung starten und das Objekt dem Aufruf der toString-Methode zuordnen. –

Antwort

5

Es ist möglich, dies zu tun, indem Sie den Zugriffstyp des ID-Felds auf "Eigenschaft" festlegen. wie:

@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 
} 

Es wird here erklärt. Auf diese Weise wird das ID-Feld immer gefüllt, wenn ein Proxy erstellt wird.

+0

+1 Ich mag es; Es gibt nur einen kleinen Haken: Ich benutze die DSL-Syntax, daher heißt mein Getter "id()", nicht "getId()". Ich denke, ich könnte einen zweiten Getter für diesen speziellen Fall hinzufügen, aber vielleicht ist es möglich, Hibernate den Namen des Getters zu nennen? –

+0

Nun, es ist möglich, indem Sie Ihre eigene Implementierung von org.hibernate.property.PropertyAccessor erstellen und den vollständig qualifizierten Namen als Wert für @AccessType deklarieren. Auf der anderen Seite könntest du den Setter (du wirst das auch brauchen) und Getter erstellen und sie privat machen, damit du sie nicht aus dem Rest deiner App siehst. – EJB

+0

@EJB: Hat es wirklich funktioniert? Ich habe eine Situation, in der Klasse A -----> (hat eine Eins zu viele Rel) mit Klasse B. Beide A und B haben mehrere Eigenschaften. Wenn ich also einen toString() -Methodenaufruf für Klasse A mache, scheitert es mit der LazyInitialization-Ausnahme (wie oben) trotz der Einstellung von @AccessType ("property") für das Id-Feld der Klasse A. – dirai

0

Wenn alles, was Sie zurückgeben möchten, die ID des Objekts ist, stelle ich mir vor, getID() aufzurufen, dann analysieren Sie den Wert int/long als String in dem Moment, in dem sie angezeigt werden sollen. So scheint es zumindest auf der Grundlage der Frage zu sein.

EDIT

How to solve the LazyInitializationException using JPA and Hibernate

Nach den Kommentar sehen und einige Benutzer tun Ich glaube, dies zu Ihrem Szenario die meisten von Vorteil sein kann.

+0

Das wird die LazyInitializationException werfen, weil die Referenz noch nicht gelöst wurde. –

+0

Aaron, nach dem Lesen dieses Kommentars habe ich meinen Beitrag bearbeitet. Bitte sehen Sie sich die neuen Informationen an und lassen Sie mich wissen, ob dies das Problem löst. – Woot4Moo

+0

@Woot - Die Antworten werden nicht helfen. Die faul initialisierten Werte werden niemals gelesen. Der TX ist festgeschrieben. Die Verbindung wurde geschlossen. –

1

Ich habe eine Abhilfe gefunden:

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 "???"; 
    } 
} 

Also, was dieser Code tut, ist es die ID holt (eine 64-Bit-Nummer) vom Objekt. DBObject ist eine Schnittstelle, die long id() definiert. Wenn das Objekt ein Hibernate-Proxy ist, dann spreche ich mit seinem LazyInitializer, um die ID zu erhalten. Ansonsten rufe ich id() an. Verwendung:

0

Ich habe den Weg gefunden, der am besten zu einer guten Übung passt, ist eine Modifikation der in diesem Blog gefundenen Lösung: http://www.nonfunc.com/2016/02/05/jpa-performance-gotcha-tostring-really/. Es musste für Nullable-Felder und für Collections-Objekte geändert werden.

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

Ich sehe, was Sie dort tun . Einige Kommentare: Das Mischen von 'String.format()' und '+' ist schlecht für die Leistung. Sie sollten auch '[null]' durch '' ersetzen, da der Proxy nicht null ist - er ist einfach nicht geladen. Ein noch besserer Ansatz wäre es, '' zurückzugeben, wobei 'TYPE' der Entitätstyp und 'ID' der Primärschlüssel ist. –

+0

Bearbeiteter Code für die Leistung. Das Beispiel war nicht genau das, was ich habe und die Verkettung einer Variablen und eines Strings wirkt sich auf die Leistung aus (die Formatzeichenfolge sollte in eine statische Zeichenkette kompiliert werden), aber ich denke, dass das Schwitzen über die Leistung eines toString nicht besonders wichtig ist. da es zum Debuggen oder zur Fehlerberichterstattung verwendet würde und in Produktionssituationen nicht besonders häufig aufgerufen werden sollte. Ich mag den Vorschlag, faul gegen null zu spezifizieren. – Nielsvh

Verwandte Themen