-->
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.  [ 51 posts ]  Go to page 1, 2, 3, 4  Next
Author Message
 Post subject: dealing with detached POJO's over RMI-client
PostPosted: Tue Aug 23, 2005 11:28 am 
Senior
Senior

Joined: Mon Aug 22, 2005 5:45 am
Posts: 146
Hi

We have a JBoss running Hibernate3 thru JBossHibernate-JMX-MBean.
We have a Swing-client connected over RMI and POJO's generated by HibernateTools3.

Now we are facing the lazy-loading issue. When lazy is enabled, currently we get a LazyInitializationException on person.getContacts() since our POJO has been serialized and the hibernate-session-reference is gone.

The strategy we figured out is to

a) overwrite the persistent collection impl's to implement a generic access to lazy-load requested data using a session-bean reference

b) implementing generic lazy-loading-backend in our BaseSessionBean

Q: is this (or any any better solution) already availbable ?


(Currently we do not plan to use AspectJ.)


Top
 Profile  
 
 Post subject: LazyInitializationExc: how to lazy-load from RMI-client ?
PostPosted: Wed Aug 24, 2005 3:30 am 
Senior
Senior

Joined: Mon Aug 22, 2005 5:45 am
Posts: 146
perhaps the subject was not appropritate.
I think there must be somebody out there who is also using a swing-client over wanting to fetch referenced object-list on demand (lazy) ...

axismundi wrote:
Hi

We have a JBoss running Hibernate3 thru JBossHibernate-JMX-MBean.
We have a Swing-client connected over RMI and POJO's generated by HibernateTools3.

Now we are facing the lazy-loading issue. When lazy is enabled, currently we get a LazyInitializationException on person.getContacts() since our POJO has been serialized and the hibernate-session-reference is gone.

The strategy we figured out is to

a) overwrite the persistent collection impl's to implement a generic access to lazy-load requested data using a session-bean reference

b) implementing generic lazy-loading-backend in our BaseSessionBean

Q: is this (or any any better solution) already availbable ?


(Currently we do not plan to use AspectJ.)


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 24, 2005 5:18 am 
Newbie

Joined: Wed Mar 16, 2005 11:06 am
Posts: 10
I tried a long time to get lazy fetching to work with swing using a long session. I had strong problems with the EDT and stale data.
Today I do not use lazy fetching with an open session. I initialize the data, the gui wants to display, in the DAO. The GUI has to know when to call the DAO again to get more data.

Example:
A GUI displays a list of persons. Each person has associated collections which are fetched lazily. The DAO has two methods:
1. List<Person> getAllPersons();
returns all available persons which are all not initialized and detached from their session => no lazy fetching of the collections is possible

2. Person loadPerson(Person)
returns the fully initialized Person. In my case it is a new object. But one could also reattach the give person and refresh and initialize it.

When the GUI displays all persons in a JList, it doesn't need the associated collections and only needs to call DAO.getAllPersons().
When the user selects a person and wants to see the details, the GUI asks the DAO to load the person completely.

So, I can use lazy fetching to prevent Hibernate from loading too much data. But I have to manually reattach and initialize the rest of the data when needed without using Hibernates automatic for that.


If I would use RMI and Hibernate, I would design a similar protocol. So you are always sure to have the freshest data and you can tell your client clearly what data it can use an what not.

If you find a better solution I am very interested.

Regards
Sebastian


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 24, 2005 9:18 am 
Senior
Senior

Joined: Mon Aug 22, 2005 5:45 am
Posts: 146
Thx Sebastian for sharing your idea.

My current concept, as described in my original post, is something similar but more generic:

1. the BaseDAO (extended by all DAO's) hold a reference to the sessionBean as a member

2. I modify the generic template that generates the collection-getters in the POJOs:
for example the Set-getter looks like this:

Set getSomeSet() {
someSessionBean.initialize(contacts);
return contacts;
}

3. the BaseSessionBean implements the service to re-init the collection
void initialize(Collection c) {
Hibernate.initialize(c);
(...)
}

I will implement this scenario within the next days.

bzw. this is my first project with an RMI/hibernate and I'm quite astonished to see that there is no standard-solution for this pretty common scenario, especially since hibernate belongs already to the JBoss-group.





sebbo wrote:
I tried a long time to get lazy fetching to work with swing using a long session. I had strong problems with the EDT and stale data.
Today I do not use lazy fetching with an open session. I initialize the data, the gui wants to display, in the DAO. The GUI has to know when to call the DAO again to get more data.

Example:
A GUI displays a list of persons. Each person has associated collections which are fetched lazily. The DAO has two methods:
1. List<Person> getAllPersons();
returns all available persons which are all not initialized and detached from their session => no lazy fetching of the collections is possible

2. Person loadPerson(Person)
returns the fully initialized Person. In my case it is a new object. But one could also reattach the give person and refresh and initialize it.

When the GUI displays all persons in a JList, it doesn't need the associated collections and only needs to call DAO.getAllPersons().
When the user selects a person and wants to see the details, the GUI asks the DAO to load the person completely.

So, I can use lazy fetching to prevent Hibernate from loading too much data. But I have to manually reattach and initialize the rest of the data when needed without using Hibernates automatic for that.


If I would use RMI and Hibernate, I would design a similar protocol. So you are always sure to have the freshest data and you can tell your client clearly what data it can use an what not.

If you find a better solution I am very interested.

Regards
Sebastian


Top
 Profile  
 
 Post subject: Same here...
PostPosted: Thu Sep 22, 2005 6:39 pm 
Beginner
Beginner

Joined: Fri Jul 08, 2005 8:55 pm
Posts: 37
Hi

i am facing exactly the same issue. It very feels like Hibernate is either server-only technology (EJB/Servlet->DB) or a 2-tier one (GUI->DB).

The manipulation of detached objects served via RMI is... painful.
We have stripped the Hibernate jar to reduce the client footprint... still 585KB.

We basically return the a mini-universe object whenever something is required on the client. We were careful in designing the links between object and sometimes broke foreign keys to avoid returning the "world" everytime.

Everything was working fine until we reached the need to replace a set in a detached object.

class MyObject {
private Set mySet;
}

the GUI will manipulate that set and then call: setMySet(newSet)... and we get an exception:
Code:
rg.hibernate.LazyInitializationException: failed to lazily initialize a collection - no session or session was closed
       at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:196)
       at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:188)
       at org.hibernate.collection.AbstractPersistentCollection.write(AbstractPersistentCollection.java:66)
       at org.hibernate.collection.PersistentSet.clear(PersistentSet.java:216)
       at org.springframework.binding.value.support.BufferedCollectionValueModel.populateFromListModel(BufferedCollectionValueModel.java:228)
       at org.springframework.binding.value.support.BufferedCollectionValueModel.createCollection(BufferedCollectionValueModel.java:191)
       at org.springframework.binding.value.support.BufferedCollectionValueModel.getValueToCommit(BufferedCollectionValueModel.java:108)
       at org.springframework.binding.value.support.BufferedValueModel.commit(BufferedValueModel.java:197)
       at org.springframework.binding.value.support.BufferedValueModel$CommitTriggerHandler.commit(BufferedValueModel.java:264)
       at org.springframework.binding.value.CommitTrigger.commit(CommitTrigger.java:45)
       at org.springframework.binding.form.support.AbstractFormModel.doCommit(AbstractFormModel.java:436)
       at org.springframework.binding.form.support.AbstractFormModel.commit(AbstractFormModel.java:418)
       at org.springframework.binding.form.support.AbstractFormModel.doCommit(AbstractFormModel.java:434)
       at org.springframework.binding.form.support.AbstractFormModel.commit(AbstractFormModel.java:418)


Has anyone managed to get around this? Being a detached object, I would assume that I can change the set/list etc and send them back to the server to be saved as part of the containing object. As I said, it worked nicely until we tried to change the set...

I also feel that RMI clients are being forgotten/neglected... sorry...

Anyone could help?? I would really appreciate!

Thanks

Benoit


Top
 Profile  
 
 Post subject: Re: Same here...
PostPosted: Fri Sep 23, 2005 3:48 am 
Senior
Senior

Joined: Mon Aug 22, 2005 5:45 am
Posts: 146
Quote:
i am facing exactly the same issue. It very feels like Hibernate is either server-only technology (EJB/Servlet->DB) or a 2-tier one (GUI->DB).
The manipulation of detached objects served via RMI is... painful.


yes I would agree.

Quote:
Everything was working fine until we reached the need to replace a set in a detached object.

class MyObject {
private Set mySet;
}



The problem ist not only with associated collections it's also with one2one relation as soon as you enable lazy loading.
The major problem is that the proxies object (pojo or collection) has lost it's session after being deserialized.

Our first step was to implement a bean-caller on the client side that is a static member of our root-pojo class. Now you can lazy-load your collection by calling an ejb:

class Person{
Set getAdresses() {
beanCaller.init(addresses);
}
}

class SomeSessionBean
{
Object init(Object){
// we still have no session here :-(((
Hibernate.initialize()
}
}

The problem is how to (re-) register the session with the object.

I tried something this:
Code:
s = HibernateUtil.createSession();
if (o instanceof AbstractPersistentCollection)
         {
            AbstractPersistentCollection c = (AbstractPersistentCollection) o;
            Object pojo = c.getOwner();
            s.merge(pojo);
            Hibernate.initialize(o);
         }
         else if (o.getClass().getName().indexOf("EnhancerByCGLIB") != -1)
         {
            Method m = o.getClass().getMethod("getHibernateLazyInitializer", new Class[] {});
            LazyInitializer li = (LazyInitializer) m.invoke(o, new Object[] {});
            Serializable id = li.getIdentifier();
            s.load(o.getClass().getSuperclass(), id);
         }


However in both cases resulted in even more trouble:
In case of the collection merge() caused a terrible recursivity which we cannot explain yet.
In case of the pojo it simply is not loading, although the id is properly passed.

If anybody has some input on these issued please give us a hint. Thx.


Top
 Profile  
 
 Post subject: not even tried that...
PostPosted: Fri Sep 23, 2005 4:23 am 
Beginner
Beginner

Joined: Fri Jul 08, 2005 8:55 pm
Posts: 37
Hi

We did not try what you're doing; I felt it was too ambitious to try to create a smart caching mechanism... so we determined, during the design of the hibernate classes, what would be "the right amount of data" to be sent from the server with each query.

This worked nicely until we faced the issue of replacing some "Sets" during a GUI manipulation. If your UI calls getMySet().clear()... you're in trouble with a LazyInitializationException as the PersistentSet requires to be in a session to do a clear, even if the object is detached!. I cannot understand why???

I ended up writing (based on an example in this forum) a server-side method that quickly goes through the objects served and replace any persistent collection with their java.util equivalent...

I wish this would be a feature of Hibernate or that the Persistent collection could work detached...

I can post the code if you want.

Regards from London

Benoit


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 27, 2005 10:49 am 
Newbie

Joined: Tue Sep 27, 2005 7:30 am
Posts: 12
Hi Benoit

I'd appreciate it if you could mail the code to do the conversion.
How much overhead will this cause?

Thanks


Top
 Profile  
 
 Post subject: here it goes
PostPosted: Tue Sep 27, 2005 11:55 am 
Beginner
Beginner

Joined: Fri Jul 08, 2005 8:55 pm
Posts: 37
Hi

No worries, here is the code, as I said it was developed by someone else, I just modified it to ensure that if the mian object is a collection that it checks for Persistent collections inside it.

Code:
    /**
     * Eliminate any reference to the Hibernate persistent collections.
     *
     * @param bean object to process
     */
    public void processHibernatedBean(final Object bean, final Session session) {
        Class type = bean.getClass();

        if (Collection.class.isAssignableFrom(type) ) {
            for(Object object : ((Collection)bean)) {
                processHibernatedBean(object, new HashSet(), session);
            }
        } else {
            processHibernatedBean(bean, new HashSet(), session);
        }
    }

    private void processHibernatedBean(final Object bean, final Set processed, final Session session) {
        if (processed.contains(bean)) {
            return;
        } else {
            try {
                session.evict(bean);
            } catch (HibernateException e) {
                getLog().warn("Error in session.evict", e);
            }
            processed.add(bean);
        }
        final BeanInfo beanInfo = getBeanInfo(bean.getClass());
        if (beanInfo == null) {
            return;
        }
        final PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (int i = 0; i < propertyDescriptors.length; i++) {
            final PropertyDescriptor propertyDescriptor = propertyDescriptors[i];
            final Class type = propertyDescriptor.getPropertyType();
            Object value;
            try {
                value = propertyDescriptor.getReadMethod().invoke(bean, new Object[0]);
            } catch (Exception e) {
                getLog().warn("Error reader property " + propertyDescriptor.getDisplayName(), e);
                value = null;
            }
            if (value != null) {
                final Class clazz = value.getClass();
       
                if ((Collection.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type))
                        && clazz.getName().startsWith("org.hibernate")) {
   
                    final Object newValue;
                    final boolean inicialitzat = Hibernate.isInitialized(value);
                    switch (getCollectionType(clazz)) {
                        case TYPE_LIST:
                            newValue = inicialitzat ? new LinkedList<Object>((Collection) value) : Collections.EMPTY_LIST;
                            break;
                        case TYPE_SET:
                            newValue = inicialitzat ? new HashSet<Object>((Collection<Object>) value) : Collections.EMPTY_SET;
                            break;
                        case TYPE_MAP:
                            newValue = inicialitzat ? new HashMap<Object,Object>((Map<Object,Object>) value) : Collections.EMPTY_MAP;
                            break;
                        default:
                            getLog().info("Tipus desconegut? " + clazz.getName());
                            newValue = null;
                            break;
                    }
                    try {
                        if (getLog().isDebugEnabled()) {
                            getLog().debug("|---Replaced with --------->" + newValue.getClass().getName());
                        }
                        propertyDescriptor.getWriteMethod().invoke(bean, new Object[] { newValue });
                    } catch (Exception e) {
                        getLog().warn("Error changing property", e);
                    }
                    if (inicialitzat) {
                        Collection values = Collection.class.isAssignableFrom(type) ? (Collection) newValue
                                : ((Map) newValue).values();
                        for (Iterator iterator = values.iterator(); iterator.hasNext();) {
                            final Object o = iterator.next();
                            if (o != null) {
                                if (ValueObject.class.isAssignableFrom(o.getClass())) {
                                    processHibernatedBean(o, processed, session);
                                }
                            }
                        }
                    }
                } else {
                    if (ValueObject.class.isAssignableFrom(type)) {
                        if (Hibernate.isInitialized(value)) {
                            processHibernatedBean(value, processed, session);
                        } else {
                            Object newValue = null;
                            try {
                                Class newClazz = HibernateProxyHelper.getClassWithoutInitializingProxy(value);
                                newValue = newClazz.newInstance();
                                ClassMetadata clazzMetaData = session.getSessionFactory().getClassMetadata(newClazz);
                                Serializable id = clazzMetaData.getIdentifier(value, EntityMode.POJO);
                                clazzMetaData.setIdentifier(newValue, id, EntityMode.POJO);
                            } catch (Exception e) {
                                getLog().warn(
                                        "Error instantiating lazy val:" + value + " newClazz:"
                                                + newValue.getClass().getName(), e);
                            }
                            try {
                                propertyDescriptor.getWriteMethod().invoke(bean, new Object[] { newValue });
                            } catch (Exception e) {
                                getLog().warn("Error finding property write", e);
                            }
                        }
                    }
                }
            }
        }
    }


Note that this code was also slightly modified for JDK 1.5 but should work with anything.

So, say you have object myObject to return via RMI you simply call:

processHibernatedBean(myObject, theHibernateSession)

Also, ALL your objects that may contain persistent collections should implement an empty interface "ValueObject".

I have not measured the impact, it is still very fast and it removed a showstopper for me so...

Good luck!

Benoit


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 29, 2005 9:05 am 
Newbie

Joined: Tue Sep 27, 2005 7:30 am
Posts: 12
Thanks Benoit, I really appreciate it.

I've got the JDK 1.4 version if anyone needs it.

Naeem


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 29, 2005 10:10 am 
Beginner
Beginner

Joined: Fri Jul 08, 2005 8:55 pm
Posts: 37
No worries!

I personally think that an 'official' version should be added to Hibernate as a helper class...

My £0.02 suggestion...

Benoit


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 29, 2005 11:40 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 6:10 am
Posts: 8615
Location: Neuchatel, Switzerland (Danish)
As long as you are aware of the consequences then fine - but this will not go into the core since its freaking dangerous and can result in dataloss.....dont reattach objects to a session that has been through this code.

_________________
Max
Don't forget to rate


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 29, 2005 11:51 am 
Beginner
Beginner

Joined: Fri Jul 08, 2005 8:55 pm
Posts: 37
Max

Thanks for your reply. I agree with you, it is rather dangerous but it seems the *only* way for me, may be you can confirm:

EJB server is sending back Hibernate objects back to the GUI via RMI.
This requires the hibernate jars in the GUI client since some objects are using Hibernate Persistent Collection classes. I can live with that (but would prefer a smaller hibernate-client.jar)

Having said that, it is a *showstopper* as soon as my GUI tries to add a new collection to my object.
MyClass {
private List list;

List getList();
coid setList(List...);
}

calling setList( new java.util.ArrayList() )
then throws an exception "LazyInitializationException"...as it tries to re-initialize the existing via clear() and as far as I can see there is no way I could workaround that but to get rid of all Hibernate collection classes... right?

Is there another way?

Thanks

Benoit.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 29, 2005 11:57 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 6:10 am
Posts: 8615
Location: Neuchatel, Switzerland (Danish)
we will hopefully get a hibernate-client.jar at some point (Kabir Kahn has done work on this for the ejb3 client in jboss).

If you want to operate on the collections on the client side you should load them - so simply initialize them before sending them to the client, no need to make it a non-persistent collection.

And doing setList(new ArrayList()); is not good - do getList().add(x) or have mutator methods addX(x)/removeX(x)/clearX() instead.
Replacing the persistent collection with a non persistent empty collection will force hibernate to reinitialize the database rep if you try to reassociate the object to a session.

_________________
Max
Don't forget to rate


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 29, 2005 11:59 am 
Senior
Senior

Joined: Mon Aug 22, 2005 5:45 am
Posts: 146
you don't need to manually convert the collections-proxies to java.util.collections.
To lazy load collections from our RMI-client, this is what we did:

1. implement a logic in our entity-collection getters:
Code:
public final Set getStRollen()
    {
      if (isClient() && isUninitializedProxy(stRollen) )
       {
         stRollen = (Set)
            getRMILazyLoader().initialize(stRollen);
      }
      return this.stRollen;
    }


2. we call a session bean on the server to init the collection:
Code:
public Object initialize(Object o)
   {
      Session s = null;
      try
      {
         if(o instanceof AbstractPersistentCollection)
         {
            AbstractPersistentCollection c = (AbstractPersistentCollection) o;

            SessionImplementor newSession = (SessionImplementor) HibernateUtil.createSession();
            RootPojo owner = (RootPojo) c.getOwner(); // not a proxy !!
            String collectionName = c.getCollectionSnapshot().getRole();
            if (HibernateUtil.hasOrphanDelete(collectionName))//vtKonfAusstattungs
            {
               EntityPersister p = newSession.getEntityPersister(owner
                     .getClass().getName(), owner);
               EntityKey ownerKey = new EntityKey(HibernateUtil
                     .getDeclaredIdentifier(owner), p, EntityMode.POJO);
               
               newSession.getPersistenceContext().addEntity(ownerKey, owner);
            }

            LeaswareReattachVisitor visitor = new LeaswareReattachVisitor(
                  newSession);
            visitor.reattachCollectionProxy(c);
            c.setCurrentSession(newSession);
            c.forceInitialization();

         }
         else
            if(o.getClass().getName().indexOf("CGLIB") != -1)
            {
               logger.debug("LazyLoading:association"
                     + o.getClass().getSuperclass().getName());
               s = HibernateUtil.createSession();
               Method m = o.getClass().getMethod("getHibernateLazyInitializer",
                     new Class[] {});
               LazyInitializer li = (LazyInitializer) m.invoke(o,
                     new Object[] {});
               Serializable id = li.getIdentifier();
               o = s.get(o.getClass().getSuperclass(), id);
            }

      }


this works pretty well. We are currently fine-tuning the orphan-deletes.


benoitx wrote:
Max

Thanks for your reply. I agree with you, it is rather dangerous but it seems the *only* way for me, may be you can confirm:

EJB server is sending back Hibernate objects back to the GUI via RMI.
This requires the hibernate jars in the GUI client since some objects are using Hibernate Persistent Collection classes. I can live with that (but would prefer a smaller hibernate-client.jar)

Having said that, it is a *showstopper* as soon as my GUI tries to add a new collection to my object.
MyClass {
private List list;

List getList();
coid setList(List...);
}

calling setList( new java.util.ArrayList() )
then throws an exception "LazyInitializationException"...as it tries to re-initialize the existing via clear() and as far as I can see there is no way I could workaround that but to get rid of all Hibernate collection classes... right?

Is there another way?

Thanks

Benoit.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 51 posts ]  Go to page 1, 2, 3, 4  Next

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.