Du verwendest einen veralteten Browser. Es ist möglich, dass diese oder andere Websites nicht korrekt angezeigt werden. Du solltest ein Upgrade durchführen oder ein alternativer Browser verwenden.
JPA org.hibernate.LazyInitializationException / Spring @Transactional
Hi,
ich erhalte folgende Error-message (s.u.), wenn ich auf meine getChildren() Methode meiner TreeNode-Klasse zugreifen will. Hier der Code:
Code:
@Entity
@Transactional
public class TreeNode implements Serializable{
@Id
@GeneratedValue
protected Long id;
protected String name="";
@ManyToOne
protected Device device = null;
@ManyToOne
@JoinColumn(name="parent_id")
protected TreeNode parent = null;
@OneToMany(mappedBy="parent", fetch = FetchType.LAZY)
protected Set<TreeNode> children = new HashSet<TreeNode>();
protected TreeNode(){}
....
@Transactional
public Set<TreeNode> getChildren(){
return this.children;
...
}
Code:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
@EnableTransactionManagement
public static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
HttpSessionSecurityContextRepository securityContextRepository = new HttpSessionSecurityContextRepository();
SecurityContextPersistenceFilter filter = new SecurityContextPersistenceFilter(securityContextRepository);
http
.csrf().disable() // Use Vaadin's CSRF protection
.addFilter(filter);
}
}
Und hier die Exception:
Code:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: x.z.y.data.TreeNode.children, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:587) ~[hibernate-core-5.0.9.Final.jar:5.0.9.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:204) ~[hibernate-core-5.0.9.Final.jar:5.0.9.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:148) ~[hibernate-core-5.0.9.Final.jar:5.0.9.Final]
at org.hibernate.collection.internal.PersistentSet.size(PersistentSet.java:143) ~[hibernate-core-5.0.9.Final.jar:5.0.9.Final]
at x.y.z.data.TreeNode.getChildren(TreeNode.java:277) ~[classes/:na]
Du greifst außerhalb einer Transaktion auf getChildren() zu, das Set kann dann nicht mehr Lazy instanziiert werden. Fixen könnte man das mit FetchType Eager (allerdings hast du explizit Lazy gesetzt), vermutlich wäre es daher sinnvoller, deine Transaktionen passend zu setzten.
@Transactional an den Entitys hat nicht viel Sinn, das sollte ab die Services dran.
Wie mrBrown schon erwähnt hat, wird die Annotation @Transactional nicht in einer Entity verwendet, sondern gehört in den Service, der mit den Entitäten arbeitet...
Wie ich sehe, arbeitest du mit Hibernate, ohne einem Framework drum herum. Ich kann dir da nur EclipseLink ans Herz legen. Dieses unterstützt LAZY Loading. Mit plain Hibernate ist es etwas tricki. Schaue dir mal diesen etwas älteren Artikel hierzu an, damit du evtll. verstehst, was da im Hintergrund passiert. http://www.empulse.de/2014/08/08/eclipselink-bessere-hibernate/
Wie ich sehe, arbeitest du mit Hibernate, ohne einem Framework drum herum. Ich kann dir da nur EclipseLink ans Herz legen. Dieses unterstützt LAZY Loading. Mit plain Hibernate ist es etwas tricki. Schaue dir mal diesen etwas älteren Artikel hierzu an, damit du evtll. verstehst, was da im Hintergrund passiert.
Er arbeitet schon mit Spring, das dürfte noch weiter abstrahieren als EclipseLink. Direkt mit Hibernate kommt er da überhaupt nicht in Berührung (und Lazy Loading ist auch absolut kein Problem)
"kann" ist nicht ganz richtig. Die JPA Spezifikation schreibt das Verhalten hier nicht explizit vor. EclipseLink bspw unterstützt unter gewissen Bedingungen LazyLoading auch außerhalb (bzw nach beenden) der Transaction. Nur als kleine Anmerkung. Das Problem hast du natürlich richtig angemerkt, und EclipseLink nutzt er ja nunmal nicht. Und den Hinweis, statt Hibernate auf EclipseLink umzusteigen, finde ich genau wie du auch quatsch. DAS ist sicherlich kein Grund der dafür spricht..
Mit tiefen Baumstrukturen, die komplett geladen werden müssen, hab ich mit JPA aber eigentlich immer schlechte Erfahrungen gemacht, was die Performance angeht, da mir da immer zu viele aufeinanderfolgende Datenbankabfragen erzeugt wurden. Ich lade in vielen Fällen einfach die Informationen über die Relationen von Parents zu Childs aus passenden Join Tables manuell und setze die Relationen dann selbst. Ist relativ straight forward und geht mit nur 2-3 Datenbankabfragen und mit dem setzen der Relationen in linearer Zeit auch "rasend schnell".
Hi,
vielen Dank für die Hinweise. Ich werde mich da jetzt mal genauer mit der Lösung beschäftigen. Ich hatte bisher auch eine EAGER Strategie, aber beim DB-Update einzelner Nodes ging die Perfomance extrem in den Keller, was ich durch die LAZY-Strategie beheben wollte.