Willkommen bei Entwickler-Forum.
Seite 1 von 2 1 2 LetzteLetzte
Ergebnis 1 bis 10 von 19
  1. #1
    Neuer Benutzer
    Registriert seit
    26.03.2008
    Beiträge
    4

    Standard Hibernate Preload Pattern

    Im JavaMagazin 4.08 wurde das Preload Pattern für Hibernate beschrieben. Das Pattern würde sich für meine Zwecke gut eignen, allerdings habe ich Probleme bei der Umsetzung.

    Ich habe den Code eigentlich 1 zu 1 übernommen wie er im Artikel abgebildet ist. Wenn ich aber nun die Methode findAll() aufrufe dann initialisiert er die Objekte trotzdem nicht so wie erhofft.

    Der Grund liegt in folgenden Codezeilen die eigentlich eine Endlosrekursion verhindern sollen:

    Code:
    if(Hibernate.isInitialized(entity)) {
       return;
    }
    Beim Aufruf von findAll() wird per crit.list() die Liste der Entities geholt und dann damit preload() aufgerufen. Allerdings ist der Aufruf von Hibernate.isInitialized() mit dieser Liste true und preload kehrt unverrichteter Dinge zurück.

    Habe ich einen Denkfehler, oder bin ich da auf einen Fehler im Artikel gestoßen?

    lg, gurks

  2. #2
    Zaungast
    Registriert seit
    02.05.2007
    Beiträge
    29

    Standard

    hi,

    was für Entities holst du aus dieser Liste?
    platziere hir ein bisschen mehr code, dann wird es leichter dein problem zu verstehen.

  3. #3
    Neuer Benutzer
    Registriert seit
    26.03.2008
    Beiträge
    4

    Standard

    Hallo,

    Ups, im Forum-Enthusiasmus bin ich wohl etwas ungenau geworden Aber kein Problem, dann werd ich mal ein wenig ausführlicher werden.

    Entität:
    Code:
    @Entity
    public class User extends PersistentEntity {
    	
    	private String login = null;
    	
    	private String encryptedPassword = null;
    
    	public User() {
    
    	}
    
    	...Getter und Setter.
    
    }
    Generisches DAO:
    Code:
    public class GenericDAOHibernate<T extends PersistentEntity, ID extends Serializable> {
    
    	...
    
    	public List<T> findAll(Preload[] preloads) {
    		return findByCriteria(preloads);
    	}
    
    	protected List<T> findByCriteria(Preload[] preloads, Criterion... criterion) {
    		Criteria crit = getSession().createCriteria(getPersistentClass());
    		for (Criterion c : criterion) {
    			crit.add(c);
    		}
    
    		List<T> result = crit.list();
    		preload(result, preloads);
    		return result;
    	}
    
    	...
    
    	// Code aus Java-Magazin 4.08
    	protected void preload(Object entity, Preload[] preloads) {
    		if (entity == null) {
    			return;
    		}
    
    		if (Hibernate.isInitialized(entity)) {
    			return;
    		}
    
    		Hibernate.initialize(entity);
    
    		if ((preloads == null) || (preloads.length == 0)) {
    			return;
    		}
    
    		if (entity instanceof Collection) {
    			for (Object resultEntity : (Collection<?>) entity) {
    				preload(resultEntity, preloads);
    			}
    		} else {
    			for (Preload preload : preloads) {
    				if (preload.getModelClass().isInstance(entity)) {
    					Object getterResult = invokeGetter(entity, preload);
    					preload(getterResult, preloads);
    				}
    			}
    		}
    	}
    
    	...
    
    }
    Mein Problem tritt nun bei folgender Verwendung des oben gezeigten Codes auf (Anm.: UserDAOHibernate extended GenericDAOHibernate):

    Code:
    UserDAOHibernate uDAO = new UserDAOHibernate(session);
    List<User> users = uDAO.findAll(preloads);
    Beim Aufruf von findAll wird nach dem Laden der User-Entitäten aus der Datenbank die Methode preload mit der geladenen Liste als Parameter aufgerufen. Bei der fett markierten Stelle kehrt preload jedoch schon zurück, ohne eventuelle Preloads durchzuführen. Ich sehe irgendwie nicht, wo der Fehler liegen könnte. Es scheint, als wäre jede gerade aus der DB geladene Liste bereits initialisiert und deshalb "bricht" preload ab.

    Hoffe mein Problem ist jetzt etwas klarer geworden.

    lg, gurks

  4. #4
    Zaungast
    Registriert seit
    02.05.2007
    Beiträge
    29

    Standard

    hi,

    sorry für spätere antwort, ich war im urlaub.

    so wie ich deine klasse 'user' verstehe, hat 'user' keine relationen zu anderen persistenten objekten. ich gehe davon aus (ich bin mir sogar zu 100 % sicher), dass an dieser stelle
    List<T> result = crit.list()
    alles aus db geladen wird bzw. 'user'-objekt und seine attribute werden initialisiert. deswegen wird an dieser stelle
    if (Hibernate.isInitialized(entity)) {
    return;
    }
    korrekterweise abgebrochen.

    angenommen hätte dein 'user' noch ein attribut 'adresse' vom typ Adresse
    public class User extends PersistentEntity {

    private String login = null;

    private String encryptedPassword = null;

    private Adresse adresse;
    dann würde adresse nachgeladen, wenn das nachladen von adresse bereits nicht im hibernate-mapping xml-datei vorkonfiguriert ist.

  5. #5
    Neuer Benutzer
    Registriert seit
    26.03.2008
    Beiträge
    4

    Standard

    Hallo,

    Danke für Deine Antwort. Soweit wär mir das klar. Aber hab das nun ausprobiert mit der hinzugefügten Liste und Hibernate.isInitialize() reagiert leider immer noch nicht so wie gewünscht. Also obwohl die Liste noch nicht initialisiert ist (auch nachgebrüft durch Debugging), kehrt die preload-Methode zu früh zurück.

    Allerdings habe ich mir jetzt selbst geholfen und hab das Pattern ohne Hilfe des Hibernate.isInitialized() implementiert, indem ich mir in einem Set merke, welche Objekte ich bereits mit preload "besucht" habe. Das funkioniert auch sehr gut. Einziger Nachteil bzw. Unschönheit: Ich muss vor jeder Operation, welche Teile vorausladen soll das Set leeren.

    Hier noch schnell der Code für Interessierte:

    Code:
    public abstract class GenericDAOHibernate<T, ID extends Serializable>
    		implements GenericDAO<T, ID> {
    
    	private Class<T> persistentClass;
    
    	private Session session;
    
    	private Set<Object> visited = null;
    
    	@SuppressWarnings("unchecked")
    	public GenericDAOHibernate() {
    		this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
    				.getGenericSuperclass()).getActualTypeArguments()[0];
    		this.visited = new HashSet<Object>();
    	}
    	
    	...
    	
    	@SuppressWarnings("unchecked")
    	public List<T> findAll(Preload[] preloads) {
    		return findByCriteria(preloads);
    	}
    	
    	@SuppressWarnings("unchecked")
    	protected List<T> findByCriteria(Preload[] preloads, Criterion... criterion) {
    		Criteria crit = getSession().createCriteria(getPersistentClass());
    		for (Criterion c : criterion) {
    			crit.add(c);
    		}
    
    		List<T> result = crit.list();
    		visited.clear();]
    		preload(result, preloads);
    		return result;
    	}
    	
    	...
    	
    	protected void preload(Object entity, Preload[] preloads) {
    		if (entity == null) {
    			return;
    		}
    
    		// solution given in Java Magazin 4.08 ... did not work
    		// if (Hibernate.isInitialized(entity)) {
    		//    return;
    		// }
    
    		if (visited.contains(entity)) {
    			return;
    		}
    
    		Hibernate.initialize(entity);
    		visited.add(entity);
    
    		if ((preloads == null) || (preloads.length == 0)) {
    			return;
    		}
    
    		if (entity instanceof Collection) {
    			for (Object resultEntity : (Collection<?>) entity) {
    				preload(resultEntity, preloads);
    			}
    		} else {
    			for (Preload preload : preloads) {
    				if (preload.getModelClass().isInstance(entity)) {
    					Object getterResult = invokeGetter(entity, preload);
    					preload(getterResult, preloads);
    				}
    			}
    		}
    	}	
    }
    Vielleicht ist es nicht die schönste Lösung, aber sie funktioniert

    lg, gurks

  6. #6
    Neuer Benutzer
    Registriert seit
    07.04.2008
    Beiträge
    8

    Standard

    Hallo gurks,

    ich denke, Du hättest Dir das mit der Liste sparen können. Es werden eh nur die Preloads durchlaufen, es kommt zu keiner Endlosschleife.

    Die Abfrage "isInitialized" ist denke ich eher aus Performanz-Gründen an der Stelle. Leider werden Collections die man sich per "findAll" oder so holt, als initialisiert angesehen, d.h. der Check liefert true. Das Auskommentieren der folgenden Zeilen hätte also genügt:

    Code:
    // solution given in Java Magazin 4.08 ... did not work
    // if (Hibernate.isInitialized(entity)) {
    //    return;
    // }
    Für mich hat das auf jeden Fall bisher funktioniert. Falls jemand anderer Meinung ist, darf er mich gerne korrigieren.

    Kleiner Zusatz:
    Wenn man sich ein einzelnes Objekt holt, und dieses enthält eine Collection, dann tritt das Problem übrigens von Haus aus auch mit dem Originalcode nicht auf!

    Was mich auch interessiert ist, wieso kommt Code, der so offensichtlich nicht korrekt funktioniert ins Java-Magazin?
    Vielleicht machen ja wir selber was falsch? Kann ich mir an der Stelle allerdings nicht vorstellen.
    Geändert von amayr (07.04.2008 um 11:21 Uhr)

  7. #7
    Zaungast
    Registriert seit
    02.05.2007
    Beiträge
    29

    Standard

    hi,

    ich habe zwar dies nicht ausprobiert, aber die vorgestellte implementierung kam mir auch irgendwie nicht ganz sauber vor . muss mal nachmachen

    Wenn man sich ein einzelnes Objekt holt, und dieses enthält eine Collection, dann tritt das Problem übrigens von Haus aus auch mit dem Originalcode nicht auf!
    genau

    ... wieso kommt Code, der so offensichtlich nicht korrekt funktioniert ins Java-Magazin?
    dann brauchen sie neben einem redakteur auch einen tester.
    den job könnte ich gern übernehmen

  8. #8
    Neuer Benutzer
    Registriert seit
    07.04.2008
    Beiträge
    8

    Standard

    Zitat Zitat von jado Beitrag anzeigen
    dann brauchen sie neben einem redakteur auch einen tester.
    den job könnte ich gern übernehmen
    Naja das wäre eigentlich die Aufgabe des Autors.

  9. #9
    Neuer Benutzer
    Registriert seit
    26.03.2008
    Beiträge
    4

    Standard

    Zitat Zitat von amayr Beitrag anzeigen
    Das Auskommentieren der folgenden Zeilen hätte also genügt:

    Code:
    // solution given in Java Magazin 4.08 ... did not work
    // if (Hibernate.isInitialized(entity)) {
    //    return;
    // }
    Ich denke, die Zeile ist deshalb drin, um Endlosschleifen beim Preload zu verhindern. So steht es jedenfalls im Artikel. Aber wie gesagt, es hat bei mir halt nicht funktioniert Vielleicht hab ich's auch einfach nicht zu 100% verstanden.

  10. #10
    Neuer Benutzer
    Registriert seit
    10.04.2008
    Beiträge
    2

    Standard

    Hi amyr,

    wie es aussieht, verwendest du den EntityManager von Hibernate. Habe mal einen Schnellversuch damit gemacht. Es ergeben sich hier tatsächlich Unterschiede zu Native-Hibernate. Bis jetzt haben wir noch nie JPA eingesetzt.

    Die Lösung mit dem Set ist übrigens eine unserer Alternativen, die wir auch schon verwendet haben.

    Das Hibernate.isInitialized() ist nicht nur eine Performanceoptimierung. Etwas in der Art wird immer benötigt, wenn die zu preloadenden Entitys eine geschlossene Schleife bilden können. Dann entsteht eine Endlosschleife.
    Wie gesagt, das mit dem Set ist auch in Ordnung. Können keine Endlosschleifen auftreten, kann man das auch ganz weglassen.

    Inzwischen haben wir das Verfahren allerdings ganz umgestellt und arbeiten mit Hibernate Events.
    Wir klinken einen PostLoadEventListener ein, etwa so:
    Code:
    public class PreloadEventListener extends DefaultPostLoadEventListener {
    
      private static ThreadLocal<Preload[]> preloadsThreadLocal = new ThreadLocal<Preload[]>();
    
      public static void setPreloads(Preload[] preloads) {
        preloadsThreadLocal.set(preloads);
      }
    
      public static void clearPreloads() {
        preloadsThreadLocal.set(null);
      }
    
      protected Object callGetter(Object entity, Preload preload) {
    
        try {
          return preload.callGetter(entity);
        } catch (Exception ex) {
          String msg = "Can't invoke getter for property: " + preload.getProperty();
          throw new PreloadException(msg, ex);
        }
      }
    
      public void onPostLoad(PostLoadEvent event) {
    
        Object entity = event.getEntity();
    
        Preload[] preloads = preloadsThreadLocal.get();
    
        if( preloads != null ) {
          for (Preload preload : preloads) {
            if (preload.getModelClass().isInstance(entity)) {
              Object getterResult = callGetter(entity, preload);
              Hibernate.initialize(getterResult);
            }
          }
        }
    
        super.onPostLoad(event);
      }
    }
    Im DAO wird dann z.B. in findByCriteria folgender Code eingeführt.

    Code:
    ...
    PreloadEventListener.setPreloads(preloads);
    result = crit.list();
    PreloadEventListener.clearPreloads();
    ...
    Das ganze hat den großen Vorteil, dass sich Hibernate selbst um das Rekursionsproblem kümmert. Allerdings ist der Code bei uns noch nicht produktiv. Bis jetzt sieht's aber gut aus.

    Gruß

    Jürgen

 

 
Seite 1 von 2 1 2 LetzteLetzte

Stichworte

Lesezeichen

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •