Execute @PostLoad _after_ eagerly fetching?
Asked Answered
L

3

12

Using JPA2/Hibernate, I've created an entity A that has a uni-directional mapping to an entity X (see below). Inside A, I also have a transient member "t" that I am trying to calculate using a @PostLoad method. The calculation requires access to the assosiated Xs:

@Entity  
public class A {  
    // ...
    @Transient
    int t;

    @OneToMany(orphanRemoval = false, fetch = FetchType.EAGER)  
    private List listOfX;  

    @PostLoad
    public void calculateT() {
        t = 0;
        for (X x : listOfX)
            t = t + x.someMethod();
    }
}

However, when I try to load this entity, I get a "org.hibernate.LazyInitializationException: illegal access to loading collection" error.

 at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:363)
 at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:108)
 at org.hibernate.collection.PersistentBag.get(PersistentBag.java:445)
 at java.util.Collections$UnmodifiableList.get(Collections.java:1154)
 at mypackage.A.calculateT(A.java:32)

Looking at hibernate's code (AbstractPersistentCollection.java) while debugging, I found that:

1) My @PostLoad method is called BEFORE the "listOfX" member is initialized
2) Hibernate's code has an explicit check to prevent initialization of an eagerly fetched collection during a @PostLoad:

 protected final void initialize(boolean writing) {
  if (!initialized) {
   if (initializing) {
    throw new LazyInitializationException("illegal access to loading collection");
   }
   throwLazyInitializationExceptionIfNotConnected();
   session.initializeCollection(this, writing);
  }
 }

The only way I'm thinking to fix this is to stop using @PostLoad and move the initialization code into the getT() accessor, adding a synchronized block. However, I want to avoid that.

So, is there a way to have eager fetching executed prior to @PostLoad being called? I don't know of a JPA facility to do that, so I'm hoping there's something I don't know.

Also, perhaps Hibernate's proprietary API has something to control this behaviour?

Libertinage answered 21/12, 2010 at 10:24 Comment(2)
@PostLoad works great for me on hibernate 3.5.6, but other people do indeed suffer: opensource.atlassian.com/projects/hibernate/browse/HHH-6043Nanete
+1 for the bug report. I have similar problem and upgrading the Hibernate to the newest version 4.1.1 nor to 3.5.6 did not help.Abbatial
L
0

Updated link to bug report:

https://hibernate.atlassian.net/browse/HHH-6043

This is fixed in 4.1.8 and 4.3.0 or later

Libertinage answered 26/4, 2019 at 10:45 Comment(0)
R
3

This might be too late, but hibernate seems not to support the default jpa fetchtype option

@OneToMany(orphanRemoval = false, fetch = FetchType.EAGER)

You must use the hibernate specific one:

@LazyCollection(LazyCollectionOption.FALSE)
Realism answered 19/2, 2014 at 5:46 Comment(0)
H
0

I don't know how to fix this but I think a little refactoring might help, the idea would be to move the code to a @PostConstruct

so for example your class would be:

@Entity  
public class A {  
    // ...
    @Transient
    int t;

@OneToMany(orphanRemoval = false, fetch = FetchType.EAGER)  
private List listOfX;  

@PostConstruct
public void calculateT() {
    t = 0;
    for (X x : listOfX)
        t = t + x.someMethod();
}

}

The server will call PostConstruct as soon as it has completed initializing all the container services for the bean.

Hayashi answered 22/12, 2010 at 9:37 Comment(1)
@PostConstruct doesn't help. Although it gives a chance to the container to initialize stuff, the fields I need to be initialized are not set up by the Spring container. Since @PostConstruct is called immediately after the constructor, Hibernate has not even populated fields like @Id and such.Libertinage
L
0

Updated link to bug report:

https://hibernate.atlassian.net/browse/HHH-6043

This is fixed in 4.1.8 and 4.3.0 or later

Libertinage answered 26/4, 2019 at 10:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.