Spring LazyLoading Problem in den Griff bekommen?

membersound

Bekanntes Mitglied
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:
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:
Java:
<p:dataTable var="_var" value="#{facade.persons}">
<p:column>
<h:outputText value="#{calculator.getPersonCount(_var)}" />


Die Backing Facaden:
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:
Java:
@Stateless
@Transactional
class Dao() {
	@PersistenceContext
	private EntityManager em;

	//CRUD
}

Entity:
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:

JimPanse

Bekanntes Mitglied
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.
 

membersound

Bekanntes Mitglied
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.
 

JimPanse

Bekanntes Mitglied
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
Java:
 int getPersonCount(Company c) {
        return c.getPersonList().size(); //EX
    }
1. Möglichkeit: Mit einer weiteren Abfrage
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
Java:
public class Company
{
private transient long nPerson;

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

//getter & setter
}

Abfrage
Java:
SELECT new Company(t.c,size(t.personList)) FROM Company t
 

membersound

Bekanntes Mitglied
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?
 

JimPanse

Bekanntes Mitglied
Nein Lazy loading hat den Sinn Daten erst dann zu laden wenn diese benötigt werden d.h.

JOIN FETCH

Java:
FROM Company c JOIN FETCH c.personList WHERE c.id=:id
oder
Java:
SELECT DISTINCT c FROM Company c JOIN FETCH c.personList
 

membersound

Bekanntes Mitglied
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:

JanHH

Top Contributor
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

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.
 

Ähnliche Java Themen

Neue Themen


Oben