-->
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.  [ 4 posts ] 
Author Message
 Post subject: Initialization of inverse lazy collection - best practice
PostPosted: Sat May 24, 2008 1:45 pm 
Newbie

Joined: Wed Feb 14, 2007 11:18 am
Posts: 10
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]

_________________
Tom McC.
"better to journey than to arrive"


Top
 Profile  
 
 Post subject:
PostPosted: Sat May 24, 2008 3:42 pm 
Newbie

Joined: Wed Feb 14, 2007 11:18 am
Posts: 10
I did change the Hibernate.initialize() call to make sure that I actually called a method on the Address.persons set, and that overriddes the lazy fetch on Address.persions without the delete/insert on the link table.

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.getHomeAddress().getPersons());
            return tmpPerson;
      }
      });
      
   }


This solves my problem, but if there is a better practice I would like to know about it.

_________________
Tom McC.
"better to journey than to arrive"


Top
 Profile  
 
 Post subject:
PostPosted: Sun May 25, 2008 2:53 pm 
Expert
Expert

Joined: Tue May 13, 2008 3:42 pm
Posts: 919
Location: Toronto & Ajax Ontario www.hibernatemadeeasy.com
Thanks for the update to your original post.

There is a better way, but it might come with a fight.

Why are you forced to initialize the entire object before passing it to the web tier? May I ask how the web tier is separated from the data tier? Separate servers? A separate EJB you go through? How is your web tier implemented? Struts? JSF? Regular Servlets and JSPs?

The emerging pattern to use in such scenarios is the open session in view pattern, and allowing transaction demarcation to happen at the web level. Why are you forcing transaction demarcation to happen in the data tier, especially when it is the web tier that needs the information?

Please read this section on transactions, and have the people that architected the solution read it as well. It will blow their minds!

http://www.hibernate.org/43.html

Of course it does come with some caveats. From that very tutorial on controlling transaction with Hibernate at the web tier:



Quote:
It's clear that this pattern only makes sense if you can actually use a local Session when rendering the view. In a three-tier environment the view might be rendered on the presentation virtual machine, not on the service virtual machine with the business and data access layer. Therefore, keeping the Session and transaction open is not an option. In this case you have to send the right "amount" of data to the presentation layer, so a view can be constructed with the Session already closed. It depends on your architecture if you better use Detached Objects, Data Transfer Objects, or maybe a mix of both with the Command Pattern. These options are discussed in Hibernate in Action.


The open session in view pattern is the way you should be accessing this data, so long as you don't fall into one of the few scenarios where it just isn't possible.

_________________
Cameron McKenzie - Author of "Hibernate Made Easy" and "What is WebSphere?"
http://www.TheBookOnHibernate.com Check out my 'easy to follow' Hibernate & JPA Tutorials


Top
 Profile  
 
 Post subject:
PostPosted: Mon May 26, 2008 9:15 pm 
Newbie

Joined: Wed Feb 14, 2007 11:18 am
Posts: 10
Excellent advice, and as I am the architect and the plumber, I can make the call. In fact, I have been seriously re-considering whether to use the Session in View pattern. I think what warned me off it initially was that after the view is rendered it can be some time(10-15 minutes) before an object is mutated and any changes committed back to the persistence layer.

But, as I say, I am literally in the middle of reconsidering this pattern as I refactor in preparation for a new release.

_________________
Tom McC.
"better to journey than to arrive"


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 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.