Spring LazyLoading Problem in den Griff bekommen?

Dieses Thema Spring - LazyLoading Problem in den Griff bekommen? im Forum "Application Tier" wurde erstellt von membersound, 3. März 2012.

Thema: LazyLoading Problem in den Griff bekommen? Hi, ich habe ein Lazy Loading Problem. Bisher habe ich es mit fetchType=EAGER umgangen, aber das soll keine...

  1. Hi,

    ich habe ein Lazy Loading Problem. Bisher habe ich es mit fetchType=EAGER umgangen, aber das soll keine Dauerlösung sein.
    Hier ein konstrutiertes Example. Es macht nicht sonderlich viel Sinn, zeigt aber mein Problem:
    Eine Company hat mehrere Personen. Personen sind über Komposition @OneToMany in der Company enthalten.

    Wenn ich auf die Personenliste aus JSF zugreife, bekomme ich diese Exception:
    Code (Java):
    Schwerwiegend [javax.enterprise.resource.webcontainer.jsf.context] (http--127.0.0.1-8080-4) javax.el.ELException: /tablePersons.xhtml @31,76 value=" #{calculator.getPersonCount(_var)}":
    org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: business.domain.Company.personList, no session or session was closed

    tablePersons.xhtml:
    Code (Java):
    <p:dataTable var="_var" value="#{facade.persons}">
    <p:column>
    <h:outputText value="#{calculator.getPersonCount(_var)}" />

    Die Backing Facaden:
    Code (Java):
    @Named
    @RequestScoped
    class Facade() {
        @Inject
        Dao dao;

        List<Company> companies;

        @PostConstruct
        init() {
            companies = Dao.findByNamedQuery("Companies.ALL");
        }
    }


    @Named
    @RequestScoped
    class Calculator {
        int getPersonCount(Company c) {
            return c.getPersonList().size(); //EX
        }
    }

     
    CRUD Service:
    Code (Java):
    @Stateless
    @Transactional
    class Dao() {
        @PersistenceContext
        private EntityManager em;

        //CRUD
    }
    Entity:
    Code (Java):
    @Entity
    @NamedQueries( {
        @NamedQuery(name = Company.ALL",
                    query = "
    SELECT c FROM Company c")
    })
    class Company {
        @OneToMany(cascade = CascadeType.ALL)  // fetch=FetchType.EAGER <-damit funktioniert alles problemlos
        List<Person> personList = new LinkedList<Person>();
    }

    Ich habe bereits den OpenEntityManager für Spring configuriert:

    web.xml:
    [XML] <filter>
    <filter-name>OpenEntityManagerInViewFilter</filter-name>
    <filter-class>
    org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter
    </filter-class>
    <init-param>
    <param-name>entityManagerFactoryBeanName</param-name>
    <param-value>entityManagerFactory</param-value>
    </init-param>
    </filter>
    <filter-mapping>
    <filter-name>OpenEntityManagerInViewFilter</filter-name>
    <url-pattern>/*</url-pattern>
    </filter-mapping>
    [/XML]

    Dennoch funktioniert es nicht.
    Danke für jede hilfreiche Idee!
     
    Zuletzt bearbeitet: 3. März 2012
  2. Vielleicht hilft dir das Grundlagen Training weiter --> *Klick*
  3. JOIN FETCH verwenden bzw. mit setFirstResult + setMaxResult die Ergebnisse Menge einschränken. Außerdem macht es ja eher
    Sinn die Personen liste eines Unternehmens erst bei Auswahl eines Unternehmens zu laden und nicht alle Unternehmen + deren Personenliste.
     
  4. Achso, hab ich ganz vergessen:
    Die Exception tritt eben genau dann auf, wenn ich die Zeile markiere und daraufhin der Zugriff auf die personList der markierten Company stattfindet.

    Die Ergebnismenge einzuschränken ist (momentan) noch nicht nötig. Will halt erstmal nur erreichen, dass beim Markieren der Company die Persons nachgeladen werden. Damit die Company Tabelle sich schneller aufbaut.
     
  5. Ich habe immer noch nicht ganz verstanden was du vor hast. Du willst eine Liste mit Unternehmen + die Anzahl der Personen die für das Unternehmen tätig sind anzeigen - richtig?

    Anstatt das
    Code (Java):

     int getPersonCount(Company c) {
            return c.getPersonList().size(); //EX
        }
     
    1. Möglichkeit: Mit einer weiteren Abfrage
    Code (Java):

     int getPersonCount(Company c) {
            return dao.countPerson(c); // SELECT COUNT(t.*) FROM Person t WHERE t.company=:company
        }
     
    2. Möglichkeit - in Company ein Feld einfügen
    Code (Java):

    public class Company
    {
    private transient long nPerson;

    public Company(Company c, long nPerson){
    this.nPerson = nPerson;
    ...
    }

    //getter & setter
    }

     
    Abfrage
    Code (Java):

    SELECT new Company(t.c,size(t.personList)) FROM Company t
     
     
  6. Ich hab doch oben geschrieben:
    "Hier ein konstrutiertes Example. Es macht nicht sonderlich viel Sinn, zeigt aber mein Problem".

    Ich bin mir durchaus bewusst, dass ich dieses Beispiel hier deutlich einfacher realisieren könnte.


    Es geht mir auch nicht um Companies oder Persons. Sondern nur darum, wie ich ohne LazyLoading Exception eine Kompositions-Liste (wie zB personList) laden kann.

    Denn die Companies-Liste "companies" ist in der Klasse Facade() enthalten.
    Wenn ich eine Tabellenspalte markiere, wird eine dieser Companies an eine andere Klasse (Calculator) übergeben. Dort wird dann auf die personList zugegriffen, was eben besagte LazyLoading Ex wirft.

    Ich suche also nach einer Möglichkeit, dieses Verhalten beizubehalten und trotzdem @OneToMany lazy zu nutzen, also Nachladen bei bedarf (beim markieren).


    Deine 1. Möglichkeit sieht soweit brauchbar aus. Aber, dort muss ich ja dann einen erneuten DB-Zugriff ausführen.
    Ist der Sinn und Zweck von LazyLoading nicht der, dass eben kein erneuter DB Zugriff erfolgen muss?
     
  7. Nein Lazy loading hat den Sinn Daten erst dann zu laden wenn diese benötigt werden d.h.

    JOIN FETCH

    Code (Java):

    FROM Company c JOIN FETCH c.personList WHERE c.id=:id
     
    oder
    Code (Java):

    SELECT DISTINCT c FROM Company c JOIN FETCH c.personList
     
     
  8. Ja schon klar, aber man muss das doch irgendwie an Hibernate oder Spring delegieren können? Ohne immer selbst unzählige SQL fetches zu schreiben.

    /edit: habe gerade mal den em mit extended annotiert. Damit würde es auch funktionieren, dh keine ex mehr werfen.
    @PersistenceContext(type=PersistenceContextType.EXTENDED)
     
    Zuletzt bearbeitet: 3. März 2012
  9. Also für mich sieht das ganz simpel danach aus dass Du auf eine @OneToMany-Collection einer Entity zugreifen willst, welche sich zu dem Zeitpunkt nicht im Persistenzkontext befindet. Also bei Deiner Funktion dann

    Code (Java):

     int getPersonCount(Company c) {
       Company c2=entityManager.find(Company.class, c.getId());
       return c2.getPersonList().size(); //EX
     }
     
    wobei Du natürlich da irgendwo den entityManager injecten musst. Lazy Loading geht natürlich nur wenn die Entity im Persistenzkontext ist, aber wenn da mehrere Requests zwischen liegen (wie z.B. bei einer Liste auf einer Webseite wo ein Entity angeklickt wird), dann ist sie detached und dann geht das halt nicht.
     
  10. Kostenloses Java-Grundlagen Training im Wert von 39 €
    Schau dir jetzt hier das Tutorial an und starte richtig durch!