Need help with Hibernate? Read this first:
http://www.hibernate.org/ForumMailingli ... AskForHelp
Hi,
I've run into a problem where I'm getting an exception
Caused by: net.sf.hibernate.LazyInitializationException: cannot access loading collection
the full stack trace is below, but it's got something to do with the fact I'm trying to manage my object graph in a parent/child relationship.
The example in the hibernate manual only talks about manipulating the parent.
I want to be able to also manipulate just the child, and have the parent taken care of in a consistent way.
Therefore if I have parent/child relationship between Area/Address
then as in the example I should have
class Area {
...
addAddress(Address address){
addresses.add(address);
//do some object graph management
address.setArea(this);
}
}
this works fine but I thought you should also tidy up the other end:
class Address {
...
setArea(Area area){
this.area = area;
this.area.addAddress(this);
}
}
the full code is below. I have a test case there aswell. Sometimes while the Area instance is loading, hibernate must be calling addAddress on the Area instance, which leads to a setArea(this) call, which in turn calls back on that Area's addAddress method.
It's this access which causes the exception. I read about lazy initialisation for all collections so I had a look at Hibernate.isInitialized(), and popped this into the setArea implementation:
setArea(Area area){
this.area = area;
if(Hibernate.isInitialized(area.getAddresses())){
this.area.addAddress(this);
}
}
and this solves the problem, but it's an ugly hack for sure.
I really don't want ANY dependencies like this in my domain objects.
Does anyone know a better way? Like - can I force ALL collections to be initialized?
Hibernate version:2.1
Mapping documents:
<class name="org.thorne.lettings.referencedata.api.AreaImpl" table="area">
<id name="id" column="id" unsaved-value="-1">
<generator class="identity"/>
</id>
<property name="name" column="name"/>
<property name="population" column="population"/>
<set name="addresses" cascade="all-delete-orphan" inverse="true" >
<key column="area_id_fk"/>
<one-to-many class="org.thorne.lettings.referencedata.api.AddressImpl"/>
</set>
</class>
<class name="org.thorne.lettings.referencedata.api.AddressImpl" table="address">
<id name="id" column="id" unsaved-value="-1">
<generator class="identity"/>
</id>
<property name="line1" column="line_1"/>
<property name="line2" column="line_2"/>
<property name="line3" column="line_3"/>
<property name="line4" column="line_4"/>
<property name="line5" column="line_5"/>
<property name="city" column="city"/>
<property name="postCode" column="postcode"/>
<many-to-one name="contact" class="org.thorne.lettings.referencedata.api.Contact" column="contact_id_fk" />
<many-to-one name="area" class="org.thorne.lettings.referencedata.api.AreaImpl" column="area_id_fk" cascade="save-update" />
</class>
<class name="org.thorne.lettings.referencedata.api.Contact" table="contact">
<id name="id" column="id" unsaved-value="-1">
<generator class="identity"/>
</id>
<component name="personName" class="org.thorne.lettings.referencedata.api.PersonNameImpl">
<property name="firstName" column="first_name"/>
<property name="secondName" column="second_name"/>
<property name="middleName1" column="middle_name_1"/>
<property name="middleName2" column="middle_name_2"/>
<many-to-one name="title" class="org.thorne.lettings.referencedata.api.TitleImpl" column="title_id_fk" unique="true" cascade="none" />
</component>
<one-to-one name="address" class="org.thorne.lettings.referencedata.api.AddressImpl" property-ref="contact" cascade="all" />
<one-to-one name="homeNumber" class="org.thorne.lettings.referencedata.api.TelephoneNumberImpl" cascade="all" />
<one-to-one name="workNumber" class="org.thorne.lettings.referencedata.api.TelephoneNumberImpl" cascade="all"/>
<one-to-one name="mobileNumber" class="org.thorne.lettings.referencedata.api.TelephoneNumberImpl" cascade="all"/>
<joined-subclass name="org.thorne.lettings.referencedata.api.TenantImpl" table="tenant">
<key column="id"/>
<many-to-one name="maritalStatus" class="org.thorne.lettings.referencedata.api.MaritalStatusImpl" column="marital_status_id_fk" unique="true" cascade="none" />
<property name="pets" column="has_pets"/>
<property name="petDescription" column="pet_description"/>
<property name="smoker" column="is_smoker"/>
<property name="children" column="has_children"/>
<component name="employmentDetails" class="org.thorne.lettings.referencedata.api.EmploymentDetailsImpl" >
<many-to-one name="employmentType" class="org.thorne.lettings.referencedata.api.EmploymentTypeImpl" column="employment_type_id_fk" unique="true" cascade="none" />
<property name="occupation"/>
<many-to-one name="employer" class="org.thorne.lettings.referencedata.api.EmployerImpl" column="employer_id_fk" unique="true" cascade="none" />
</component>
</joined-subclass>
<joined-subclass name="org.thorne.lettings.referencedata.api.EmployerImpl" table="employer">
<key column="id"/>
<set name="employees" cascade="none" inverse="true" >
<key column="employer_id_fk"/>
<one-to-many class="org.thorne.lettings.referencedata.api.TenantImpl"/>
</set>
</joined-subclass>
</class>
Code:
package org.thorne.lettings.referencedata.api;
import net.sf.hibernate.Hibernate;
import net.sf.hibernate.HibernateException;
/**
*@author Neil Thorne
*/
public class AddressImpl extends EntityImpl implements Address {
private String line1;
private String line2;
private String line3;
private String line4;
private String line5;
private String city;
private String postCode;
private Area area;
private Contact contact;
public AddressImpl() {
area = new AreaImpl();
}
public String getLine1() {
return line1;
}
public String getLine2() {
return line2;
}
public String getLine3() {
return line3;
}
public String getLine4() {
return line4;
}
public String getLine5() {
return line5;
}
public String getCity() {
return city;
}
public String getPostCode() {
return postCode;
}
public void setLine1(String line1) {
this.line1 = line1;
}
public void setLine2(String line2) {
this.line2 = line2;
}
public void setLine3(String line3) {
this.line3 = line3;
}
public void setLine4(String line4) {
this.line4 = line4;
}
public void setLine5(String line5) {
this.line5 = line5;
}
public void setCity(String city) {
this.city = city;
}
public void setPostCode(String postCode) {
this.postCode = postCode;
}
public Area getArea() {
return area;
}
public void setArea(Area area) {
//before we access this area we should ensure all its addresses are loaded
//into hibernate
if (this.area != area) {
Area old = this.area;
this.area = null;
if (old != null) {
if(Hibernate.isInitialized(area.getAddresses())){
old.removeAddress(this);
}
}
this.area = area;
if (this.area != null) {
if(Hibernate.isInitialized(area.getAddresses())){
this.area.addAddress(this);
}
}
}
}
public Contact getContact() {
return contact;
}
public void setContact(Contact contact) {
if (this.contact != contact) {
Contact old = this.contact;
this.contact = null;
if (old != null) {
old.setAddress(null);
}
this.contact = contact;
if (this.contact != null) {
this.contact.setAddress(this);
}
}
}
public String toString() {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("AddressImpl[");
stringBuffer.append("id=").append(id).append(",");
stringBuffer.append("line1=").append(line1).append(",");
stringBuffer.append("line2=").append(line2).append(",");
stringBuffer.append("line3=").append(line3).append(",");
stringBuffer.append("line4=").append(line4).append(",");
stringBuffer.append("line5=").append(line5).append(",");
stringBuffer.append("postCode=").append(postCode).append(",");
stringBuffer.append("city=").append(city);
stringBuffer.append("]");
return stringBuffer.toString();
}
}
package org.thorne.lettings.referencedata.api;
import java.util.*;
/**
* At the moment this will be a Peterborough geographical area
* @author Neil Thorne
*/
public class AreaImpl extends EntityImpl implements Area {
protected String name = "";
protected Integer population = new Integer(0);
private Set addresses = new HashSet();
public AreaImpl() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPopulation() {
return population.intValue();
}
public void setPopulation(int population) {
this.population = new Integer(population);
}
public Set getAddresses() {
return addresses;
}
public void setAddresses(Set addresses) {
this.addresses = addresses;
}
public void addAddress(Address address) {
this.addresses.add(address);
if(address!=null){
address.setArea(this);
}
}
public String toString() {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("AreaImpl[");
stringBuffer.append("id=" + id + ",");
stringBuffer.append("population=" + population + ",");
stringBuffer.append("name=" + name + ",");
stringBuffer.append("]");
return stringBuffer.toString();
}
public void removeAddress(Address address) {
this.addresses.remove(address);
}
}
package org.thorne.lettings.referencedata.api;
/**
*@author Neil Thorne
*/
public abstract class ContactImpl extends EntityImpl implements Contact {
protected PersonName personName;
protected Address address;
protected TelephoneNumber homeNumber;
protected TelephoneNumber workNumber;
protected TelephoneNumber mobileNumber;
public ContactImpl() {
personName = new PersonNameImpl();
}
public PersonName getPersonName() {
return personName;
}
public void setPersonName(PersonName personName) {
this.personName = personName;
}
public Address getAddress() {
return address;
}
public TelephoneNumber getHomeNumber() {
return homeNumber;
}
public TelephoneNumber getWorkNumber() {
return workNumber;
}
public TelephoneNumber getMobileNumber() {
return mobileNumber;
}
public void setAddress(Address address) {
if (this.address != address) {
Address old = this.address;
this.address = null;
if (old != null) {
old.setContact(null);
}
this.address = address;
if (this.address != null) {
this.address.setContact(this);
}
}
}
public void setHomeNumber(TelephoneNumber homeNumber) {
this.homeNumber = homeNumber;
}
public void setWorkNumber(TelephoneNumber workNumber) {
this.workNumber = workNumber;
}
public void setMobileNumber(TelephoneNumber mobileNumber) {
this.mobileNumber = mobileNumber;
}
}
import junit.framework.TestCase;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.thorne.lettings.referencedata.api.*;
import org.thorne.lettings.referencedata.service.DataManagementService;
import java.rmi.RemoteException;
/**
* @author Neil Thorne
*/
public class ContactAssociationTest extends TestCase {
private DataManagementService dataManagementService;
protected void setUp() throws Exception {
Resource resource = new ClassPathResource("data-management.xml");
ListableBeanFactory listableBeanFactory = new XmlBeanFactory(resource);
dataManagementService = (DataManagementService) listableBeanFactory.getBean("dataManagementService");
}
public void testStackOverflow4() throws RemoteException {
Area area = (Area) dataManagementService.find(AreaImpl.class, new Integer(2));
final Address address1 = new AddressImpl();
address1.setLine1("1");
address1.setCity("Bluff");
address1.setPostCode("PEY123");
address1.setArea(area);
final Address address2 = new AddressImpl();
address2.setLine1("2");
address2.setCity("Bluff");
address2.setPostCode("PEY123");
address2.setArea(area);
final Contact contact = new EmployerImpl();
contact.getPersonName().setFirstName("Neil");
contact.getPersonName().setSecondName("Grover");
Title title = (Title) dataManagementService.find(TitleImpl.class, new Integer(1));
contact.getPersonName().setTitle(title);
contact.setAddress(address1);
contact.setAddress(address2);
dataManagementService.create(contact);
dataManagementService.create(address1);
assertTrue(address1.getContact() == null);
assertTrue(contact == address2.getContact());
assertTrue(address2.getContact() == contact);
dataManagementService.delete(contact);
dataManagementService.delete(address1);
}
public void testStackOverflow3() throws RemoteException {
Area area = (Area) dataManagementService.find(AreaImpl.class, new Integer(2));
final Address address = new AddressImpl();
address.setLine1("1");
address.setCity("Bluff");
address.setPostCode("PEY123");
address.setArea(area);
final Contact contact1 = new EmployerImpl();
contact1.getPersonName().setFirstName("Neil");
contact1.getPersonName().setSecondName("Grover");
Title title = (Title) dataManagementService.find(TitleImpl.class, new Integer(1));
contact1.getPersonName().setTitle(title);
final Contact contact2 = new EmployerImpl();
contact2.getPersonName().setFirstName("Neil");
contact2.getPersonName().setSecondName("Grover III");
contact2.getPersonName().setTitle(title);
address.setContact(contact1);
address.setContact(contact2);
dataManagementService.create(contact1);
dataManagementService.create(contact2);
assertTrue(contact1.getAddress() == null);
assertTrue(contact2 == address.getContact());
assertTrue(contact2.getAddress() == address);
address.setContact(null);
address.setContact(contact2);
dataManagementService.update(contact1);
dataManagementService.update(contact2);
assertTrue(contact2.getAddress() == address);
assertTrue(address.getContact() == contact2);
dataManagementService.delete(contact1);
dataManagementService.delete(contact2);
}
public void testContactListenerAdditionToAddress() throws RemoteException {
Area area = (Area) dataManagementService.find(AreaImpl.class, new Integer(2));
final Address address1 = new AddressImpl();
address1.setLine1("10");
address1.setCity("Bluffy");
address1.setPostCode("VAMPIRE");
address1.setArea(area);
final Contact contact1 = new EmployerImpl();
contact1.getPersonName().setFirstName("Fluff");
contact1.getPersonName().setSecondName("Grover");
Title title = (Title) dataManagementService.find(TitleImpl.class, new Integer(1));
contact1.getPersonName().setTitle(title);
contact1.setAddress(address1);
dataManagementService.create(contact1);
assertTrue(address1.getContact() == contact1);
address1.setContact(null);
assertTrue(contact1.getAddress()==null);
assertTrue(address1.getContact()==null);
//can't just update the contact and expect the address to be updated too.
//this is because there is no information about the address to update -
//contact has a NULL address which means that hibernate can't cascade any
//deletes for us... we must update the address too.
dataManagementService.update(contact1);
dataManagementService.update(address1);
assertTrue(contact1.getAddress()==null);
assertTrue(address1.getContact()==null);
//dataManagementService.delete(address1);
dataManagementService.delete(contact1);
}
public void testAreaAndAddressAssociation() throws RemoteException {
final Address address1 = new AddressImpl();
address1.setLine1("1");
address1.setPostCode("wowowo");
address1.setCity("hodge");
final Area area1 = new AreaImpl();
area1.setName("Poop");
final Area area2 = new AreaImpl();
area2.setName("Scoop");
area1.addAddress(address1);
assertTrue(address1.getArea()==area1);
dataManagementService.create(address1);
address1.setArea(area2);
dataManagementService.update(address1);
assertTrue(address1.getArea() == area2);
assertTrue(area1.getAddresses().size() == 0);
assertTrue(area2.getAddresses().contains(address1));
dataManagementService.delete(address1);
}
}
Full stack trace of any exception that occurs:
org.springframework.orm.hibernate.HibernateSystemException: exception setting property value with CGLIB (set hibernate.cglib.use_reflection_optimizer=false for more info) setter of org.thorne.lettings.referencedata.api.AddressImpl.setArea; nested exception is net.sf.hibernate.PropertyAccessException: exception setting property value with CGLIB (set hibernate.cglib.use_reflection_optimizer=false for more info) setter of org.thorne.lettings.referencedata.api.AddressImpl.setArea
net.sf.hibernate.PropertyAccessException: exception setting property value with CGLIB (set hibernate.cglib.use_reflection_optimizer=false for more info) setter of org.thorne.lettings.referencedata.api.AddressImpl.setArea
at net.sf.hibernate.persister.AbstractEntityPersister.setPropertyValues(AbstractEntityPersister.java:221)
at net.sf.hibernate.impl.SessionImpl.initializeEntity(SessionImpl.java:2223)
at net.sf.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:315)
at net.sf.hibernate.loader.Loader.doQuery(Loader.java:305)
at net.sf.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:133)
at net.sf.hibernate.loader.Loader.loadCollection(Loader.java:990)
at net.sf.hibernate.loader.Loader.loadCollection(Loader.java:965)
at net.sf.hibernate.loader.OneToManyLoader.initialize(OneToManyLoader.java:93)
at net.sf.hibernate.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:288)
at net.sf.hibernate.impl.SessionImpl.initializeCollection(SessionImpl.java:3303)
at net.sf.hibernate.collection.PersistentCollection.forceInitialization(PersistentCollection.java:336)
at net.sf.hibernate.impl.SessionImpl.initializeNonLazyCollections(SessionImpl.java:3156)
at net.sf.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:138)
at net.sf.hibernate.loader.Loader.loadEntity(Loader.java:911)
at net.sf.hibernate.loader.Loader.loadEntity(Loader.java:931)
at net.sf.hibernate.loader.EntityLoader.load(EntityLoader.java:59)
at net.sf.hibernate.loader.EntityLoader.load(EntityLoader.java:51)
at net.sf.hibernate.persister.EntityPersister.load(EntityPersister.java:415)
at net.sf.hibernate.impl.SessionImpl.doLoad(SessionImpl.java:2130)
at net.sf.hibernate.impl.SessionImpl.doLoadByClass(SessionImpl.java:2000)
at net.sf.hibernate.impl.SessionImpl.load(SessionImpl.java:1929)
at org.springframework.orm.hibernate.HibernateTemplate$3.doInHibernate(HibernateTemplate.java:291)
at org.springframework.orm.hibernate.HibernateTemplate.execute(HibernateTemplate.java:243)
at org.springframework.orm.hibernate.HibernateTemplate.load(HibernateTemplate.java:289)
at org.thorne.lettings.referencedata.service.HibernateDataManagementService.find(HibernateDataManagementService.java:32)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:296)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:163)
at $Proxy0.find(Unknown Source)
at ContactAssociationTest.testContactListenerAdditionToAddress(ContactAssociationTest.java:91)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at com.intellij.rt.execution.junit2.JUnitStarter.main(JUnitStarter.java:31)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:78)
Caused by: net.sf.cglib.beans.BulkBeanException: cannot access loading collection
at org.thorne.lettings.referencedata.api.AddressImpl$$BulkBeanByCGLIB$$8c13d8c8.setPropertyValues(<generated>)
at net.sf.hibernate.persister.AbstractEntityPersister.setPropertyValues(AbstractEntityPersister.java:216)
... 55 more
Caused by: net.sf.hibernate.LazyInitializationException: cannot access loading collection
at net.sf.hibernate.collection.PersistentCollection.initialize(PersistentCollection.java:191)
at net.sf.hibernate.collection.PersistentCollection.write(PersistentCollection.java:84)
at net.sf.hibernate.collection.Set.add(Set.java:154)
at org.thorne.lettings.referencedata.api.AreaImpl.addAddress(AreaImpl.java:45)
at org.thorne.lettings.referencedata.api.AddressImpl.setArea(AddressImpl.java:100)
... 57 more
Name and version of the database you are using:MySQL 4.0.18
The generated SQL:
Hibernate: select areaimpl0_.id as id0_, areaimpl0_.name as name0_, areaimpl0_.population as population0_ from area areaimpl0_ where areaimpl0_.id=?
Hibernate: select addresses0_.area_id_fk as area_id_fk__, addresses0_.id as id__, addresses0_.id as id0_, addresses0_.line_1 as line_10_, addresses0_.line_2 as line_20_, addresses0_.line_3 as line_30_, addresses0_.line_4 as line_40_, addresses0_.line_5 as line_50_, addresses0_.city as city0_, addresses0_.postcode as postcode0_, addresses0_.contact_id_fk as contact_9_0_, addresses0_.area_id_fk as area_id_fk0_ from address addresses0_ where addresses0_.area_id_fk=?
Hibernate: select contact0_.id as id0_, case when contact0__1_.id is not null then 1 when contact0__2_.id is not null then 2 when contact0_.id is not null then 0 end as clazz_0_, contact0_.first_name as first_name4_0_, contact0_.second_name as second_n3_4_0_, contact0_.middle_name_1 as middle_n4_4_0_, contact0_.middle_name_2 as middle_n5_4_0_, contact0_.title_id_fk as title_id6_4_0_, contact0__1_.marital_status_id_fk as marital_2_5_0_, contact0__1_.has_pets as has_pets5_0_, contact0__1_.pet_description as pet_desc4_5_0_, contact0__1_.is_smoker as is_smoker5_0_, contact0__1_.has_children as has_chil6_5_0_, contact0__1_.employment_type_id_fk as employme7_5_0_, contact0__1_.occupation as occupation5_0_, contact0__1_.employer_id_fk as employer9_5_0_ from contact contact0_ left outer join tenant contact0__1_ on contact0_.id=contact0__1_.id left outer join employer contact0__2_ on contact0_.id=contact0__2_.id where contact0_.id=?
Hibernate: select titleimpl0_.id as id0_, titleimpl0_.description as descript3_0_ from type titleimpl0_ where titleimpl0_.id=?
Hibernate: select addressimp0_.id as id0_, addressimp0_.line_1 as line_10_, addressimp0_.line_2 as line_20_, addressimp0_.line_3 as line_30_, addressimp0_.line_4 as line_40_, addressimp0_.line_5 as line_50_, addressimp0_.city as city0_, addressimp0_.postcode as postcode0_, addressimp0_.contact_id_fk as contact_9_0_, addressimp0_.area_id_fk as area_id_fk0_ from address addressimp0_ where addressimp0_.contact_id_fk=?
Hibernate: select telephonen0_.id as id0_, telephonen0_.area_code as area_code0_, telephonen0_.number as number0_, telephonen0_.contact_id_fk as contact_4_0_ from telephone_number telephonen0_ where telephonen0_.id=?
Debug level Hibernate log excerpt: