Long time reader, first time asker, so please be gentle. :) I'm using Hibernate as my JPA 2.0 provider (hibernate-core-4.1.16, hibernate-jpa-1.0.1) and I'm having trouble with something seemingly simple.
Short Version
I have two entities A and B, where A holds a list of B. Every time a new list of B is set, I want to process it. Hence I put the processing inside the setter for the list. The same setter is used by Hibernate.
This works fine for loading instances of A from the database and even refreshing works when it is not cascaded. But as soon as I enable CascadeType.REFRESH and do a refresh, accessing the new list of B inside the setter, causes the infamous LazyInitializationException.
I have tried some things (see below) but nothing helped and I'm out of ideas. :(
Long Version
I created a minimal working example to show what is supposed to happen and what actually happens.
Classes
Note: I omitted the id-attributes. (In case this is important: the id-getters are annotated with @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
.)
@Entity
@Table(name = "tableA")
public class A {
private List<B> bar;
@OneToMany(mappedBy = "theA", cascade = CascadeType.ALL)
public List<B> getBar() {
return bar;
}
private void setBar(List<B> bar) {
this.bar = bar;
System.out.println("Number of Bs during set: " + bar.size());
}
}
Class B
@Entity
@Table(name = "tableB")
public class B {
private A theA;
@ManyToOne
@JoinColumn(name = "a_id", nullable = false)
public A getTheA() {
return theA;
}
private void setTheA(A theA) {
this.theA = theA;
}
}
Data
The two tables tableA and tableB each contain one row, so there will be one instance of A which holds one instance of B.
Execution
Finally, this is the executed code:
public static void main(String[] args) {
EntityManager em = Persistence.createEntityManagerFactory("TestDBManager").createEntityManager();
A theA = (A) em.createQuery("SELECT a FROM A a").getResultList().get(0);
System.out.println("Number of Bs after load: " + theA.getBar().size());
em.refresh(theA);
System.out.println("Number of Bs after refresh: " + theA.getBar().size());
}
What Should Happen vs. What Actually Happens
This is the expected outcome
Number of Bs during set: 1
Number of Bs after load: 1
Number of Bs during set: 1
Number of Bs after refresh: 1
But in fact this happens:
Number of Bs during set: 1
Number of Bs after load: 1
Exception in thread "main" javax.persistence.PersistenceException: org.hibernate.PropertyAccessException: Exception occurred inside setter of isi.power.core.test.A.bar
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1377)
at org.hibernate.ejb.AbstractEntityManagerImpl.refresh(AbstractEntityManagerImpl.java:939)
at org.hibernate.ejb.AbstractEntityManagerImpl.refresh(AbstractEntityManagerImpl.java:906)
at line of call to refresh
Caused by: org.hibernate.PropertyAccessException: Exception occurred inside setter of isi.power.core.test.A.bar
... many more
Caused by: java.lang.reflect.InvocationTargetException
... many more
Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection, no session or session was closed
... many more
What I Tried
Don't Touch the List
If I remove the calls to the list inside the setter, Hibernate finishes its magic after calling setBar
and I get no exception, when it is called later (after the refresh
-call in main
). But I loose the possibility to process the list inside the setter, which I'd rather not.
Don't Cascade Refresh
The problem goes away if I do not cascade refreshes, i.e. if I replace
cascade = CascadeType.ALL
with
cascade = { CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REMOVE }
Since I mostly do this for the refresh, this is not an option.
Fetch Eagerly
I added fetch = FetchType.EAGER
to the @OneToMany
-annotation. Same exception...
Transaction
I surrounded the load and refresh calls with em.getTransaction().begin();
and em.getTransaction().commit();
. Same exception...
Hibernate.initialize
I tried to call Hibernate.initialize
inside the setter which leads to a different exception:
Number of Bs during set: 1
Number of Bs after load: 1
Exception in thread "main" javax.persistence.PersistenceException: org.hibernate.PropertyAccessException: Exception occurred inside setter of isi.power.core.test.A.bar
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1377)
at org.hibernate.ejb.AbstractEntityManagerImpl.refresh(AbstractEntityManagerImpl.java:939)
at org.hibernate.ejb.AbstractEntityManagerImpl.refresh(AbstractEntityManagerImpl.java:906)
at line of call to refresh
Caused by: org.hibernate.PropertyAccessException: Exception occurred inside setter of isi.power.core.test.A.bar
... many more
Caused by: java.lang.reflect.InvocationTargetException
... many more
Caused by: org.hibernate.AssertionFailure: force initialize loading collection
... many more
Long Story Short
I'm fresh out of ideas... I'd be very thankful for any pointer to a possible solution of this problem.