Hibernate version: 3.1
Name and version of the database you are using: MySQL 5.0x
I have a many-to-many association between Person and Address, mapped using a link table. Persons will share an Address, and Persons sharing an Address are identified as belonging in the same household or company. Thus, Person has a Set of Address, and each Address has a Set of Person.
I always eagerly fetch the Address(es) when retrieving Person, but I don't always need to fetch all the other Person(s) mapped to the Address, so the Persons Set in Address is configured to lazy load.
Question is, when I want to fetch all associations for a Person, how can I override the lazy fetch of the Persons collection in Address? (This is a web application so I need to get everything at once to pass up to the web tier).
I tried something like this:
Code:
return (IPerson) getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session)
throws HibernateException {
Criteria crit = session.createCriteria(Person.class);
crit.add(Restrictions.eq("personId", id));
crit.setFetchMode("addresses.persons",FetchMode.JOIN);
crit.setFetchMode("donations", FetchMode.JOIN);
crit.setFetchMode("registrations", FetchMode.JOIN);
crit.setFetchMode("artistInfo", FetchMode.JOIN);
IPerson tmpPerson = (IPerson) crit.uniqueResult();
}
});
But I get an LazyInitializationException when I try to access the Address.Persons collection.
Code:
This works:
return (IPerson) getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session)
throws HibernateException {
Criteria crit = session.createCriteria(Person.class);
crit.add(Restrictions.eq("personId", id));
crit.setFetchMode("donations", FetchMode.JOIN);
crit.setFetchMode("registrations", FetchMode.JOIN);
crit.setFetchMode("artistInfo", FetchMode.JOIN);
IPerson tmpPerson = (IPerson) crit.uniqueResult();
Criteria addCrit = session.createCriteria(Address.class);
addCrit.setFetchMode("persons",FetchMode.JOIN);
addCrit.add(Restrictions.in("addressId", tmpPerson.getAddressIds()));
Set<IAddress> fullAddrs = new HashSet<IAddress>();
fullAddrs.addAll(addCrit.list());
tmpPerson.setAddresses(fullAddrs);
return tmpPerson;
}
});
The problem with this is that upon setting the Address Set in Person with the fully initialized Address objects, Hibernate deletes and then re-inserts the personId/addressId pairs into the link table. This seems fairly trivial in terms of performance but it makes me uncomfortable.
I have also tried something like the following using Hibernate.initialize() but still get a LazyInitEx.:
Code:
return (IPerson) getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session)throws HibernateException {
Query q = session.createQuery("from Person p where personId = :id ");
q.setInteger("id",id);
IPerson tmpPerson = (IPerson) q.uniqueResult();
getHibernateTemplate().initialize(tmpPerson);
return tmpPerson;
}
});
Mapping documents follow.
Mapping documents:Code:
<hibernate-mapping>
<class name="com.ravencsi.hibernate.Person" table="person"
catalog="acc">
<id name="personId" type="java.lang.Integer">
<column name="person_id" />
<generator class="identity"></generator>
</id>
<one-to-one name="memberInfo" class="com.ravencsi.hibernate.Member"
cascade="save-update" lazy="false" />
<one-to-one name="artistInfo" class="com.ravencsi.hibernate.Artist"
cascade="save-update" lazy="proxy"/>
<one-to-one name="companyInfo" class="com.ravencsi.hibernate.Company"
cascade="save-update" lazy="false"/>
<property name="lastName" type="java.lang.String">
<column name="last_name" length="255" not-null="true" />
</property>
<property name="firstName" type="java.lang.String">
<column name="first_name" length="45" />
</property>
...........
<property name="guid" type="java.lang.String">
<column name="guid" length="100" unique="true" not-null="true" />
</property>
<set name="addresses" table="person_addr" cascade="save-update"
lazy="false">
<key>
<column name="person_id" />
</key>
<many-to-many class="com.ravencsi.hibernate.Address"
column="address_id" unique="false" />
</set>
<set name="registrations" table="person_catalog"
lazy="true">
<key>
<column name="person_id" />
</key>
<many-to-many class="com.ravencsi.hibernate.Catalog"
column="catalog_id" unique="false" />
</set>
<set name="donations" inverse="true" cascade="save-update" lazy="true">
<key>
<column name="entity_id" not-null="true" />
</key>
<one-to-many class="com.ravencsi.hibernate.Donation" />
</set>
</class>
</hibernate-mapping>
Code:
<hibernate-mapping>
<class name="com.ravencsi.hibernate.Address" table="address"
catalog="acc">
<id name="addressId" type="java.lang.Integer">
<column name="address_id" />
<generator class="identity"></generator>
</id>
<property name="referenceId" type="java.lang.Integer">
<column name="reference_id" />
</property>
<property name="addressType" type="java.lang.String">
<column name="address_type" length="45" />
</property>
<property name="street1" type="java.lang.String">
<column name="street1" length="100" />
.........................................
<set name="persons" table="person_addr" inverse="true" lazy="true" batch-size="3">
<key>
<column name="address_id" />
</key>
<many-to-many class="com.ravencsi.hibernate.Person">
<column name="person_id" />
</many-to-many>
</set>
</class>
</hibernate-mapping>
Full stack trace of any exception that occurs:Code:
Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.ravencsi.hibernate.Address.persons, no session or session was closed
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:358)
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:350)
at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:343)
at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:86)
at org.hibernate.collection.PersistentSet.iterator(PersistentSet.java:163)
at com.ravencsi.hibernate.Address.getPrimary(Address.java:89)
at com.ravencsi.faces.beans.ContentBean.updateSelectedPerson(ContentBean.java:668)
at com.ravencsi.faces.beans.ContentBean.setSelectedPerson(ContentBean.java:632)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.apache.myfaces.el.MethodBindingImpl.invoke(MethodBindingImpl.java:132)
... 40 more
[/code]