-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 2 posts ] 
Author Message
 Post subject: Parent/child relationship needs Hibernate.isInitialized()?
PostPosted: Thu Nov 17, 2005 10:07 am 
Newbie

Joined: Tue Nov 15, 2005 5:41 am
Posts: 16
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:


Top
 Profile  
 
 Post subject:
PostPosted: Fri Nov 18, 2005 5:21 pm 
Newbie

Joined: Thu Nov 17, 2005 7:06 pm
Posts: 10
Setting lazy="false" might solve your problem.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 2 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.