GWT and Hibernate

386 views
Skip to first unread message

big_blue

unread,
Dec 16, 2011, 6:20:20 AM12/16/11
to Google Web Toolkit
Hi.

I know there a lot of feed about GWT and Hibernate / Spring but until
now i do not find a good answer of my question.

What i want to do is a simple web application with plain java POJOs
and database persistence with hibernate. AND and this is the main
part. I just want to have one java class for front an backend. Is this
possible?

Maybe the shared package structure is the correct place, but there
will be some problems with the serializable functionality of the
frontend. The hibernate magic will be a problem for that. It would be
very helpfull to get some imformations or an example. Maybe an info
about the package structure of a project.

THANKS

Juan Pablo Gardella

unread,
Dec 16, 2011, 9:14:58 AM12/16/11
to google-we...@googlegroups.com
See gwt-sample (Spring, JPA2)




--
You received this message because you are subscribed to the Google Groups "Google Web Toolkit" group.
To post to this group, send email to google-we...@googlegroups.com.
To unsubscribe from this group, send email to google-web-tool...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/google-web-toolkit?hl=en.


Néstor Boscán

unread,
Dec 16, 2011, 11:53:08 AM12/16/11
to google-we...@googlegroups.com
Hi Alex

I have donde this with many projects. The problem with Hibernate and
any framework that serializes POJOs (GWT, WebServices, JSON, etc) is
that Hibernate does not return POJOs, but Proxies of your POJOs. So
basically the solution to this problem is to clean your objects
returned by Hibernate to convert them to POJOs. I've seen examples
from Google that they just basically write code to create the POJO's
again and set the attributes but we prefer to use a Cleaner class that
automatically does this job.

The other problem is that you have to be careful when you save because
you are passing to hibernate an object that is not a proxy. This
causes problems when you're saving an object with collections. The
objects in the new collection will be inserted but the old objects
will not be deleted from the database. So one workaround is to load
the object from the database and set the attributes, clean the
collections (this will erase the objects from the database), and add
the new objects.

import java.beans.PropertyDescriptor;

import java.io.InputStream;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.List;

import org.apache.commons.beanutils.PropertyUtils;

import org.hibernate.EntityMode;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.proxy.HibernateProxyHelper;

import java.io.Serializable;

import java.sql.Blob;

import java.util.*;

import org.apache.log4j.Logger;

import org.hibernate.proxy.HibernateProxy;

public class HibernateCleaner {
   private static final Logger logger =
Logger.getLogger(HibernateCleaner.class);

   public static Object clean(Session session, Object obj) throws Exception {
       return (clean(session, obj, new HashMap<Class, Map<Object, Object>>()));
   }

   private static Object clean(Session session,
                               Object obj,
                               Map<Class, Map<Object, Object>>
visitedObjects) throws Exception {
       Object newObj, value = null, cleanValue = null;
       Map.Entry m;
       Class clazz;
       Object[] array;
       Collection collection;
       Map map;
       PropertyDescriptor[] descriptors;
       String property;
       ClassMetadata clazzMetaData;
       Map<Object, Object> visitedObjectsInClass;
       int index, length, hashCode;

       if (obj == null)
           return (null);

       if ((obj instanceof Boolean) || (obj instanceof Number) ||
           (obj instanceof Character) || (obj instanceof String) ||
           (obj instanceof Blob) || (obj instanceof InputStream))
           return (obj);

       if (obj instanceof Date)
           return (new Date (((Date) obj).getTime()));

       if (obj instanceof Calendar)
           return (((Calendar) obj).clone());

       if (obj instanceof Object[]) {
           array = (Object[]) ((Object[]) obj).clone();
           length = array.length;
           for (index = 0; index < length; index++)
               array[index] = clean(session, array[index], visitedObjects);

           return (array);
       }

       if (obj instanceof Object[]) {
           array = (Object[]) ((Object[]) obj).clone();
           length = array.length;
           for (index = 0; index < length; index++)
               array[index] = clean(session, array[index], visitedObjects);

           return (array);
       }

       if (obj instanceof Collection) {
           collection = createCollection((Collection) obj);

           if (Hibernate.isInitialized(obj)) {
               for (Object member: (Collection) obj)
                   collection.add (clean(session, member, visitedObjects));
           }

           return (collection);
       }

       if (obj instanceof Map) {
           map = createMap((Map) obj);

           if (Hibernate.isInitialized(obj)) {
               for (Object member: ((Map)obj).entrySet()) {
                   m = (Map.Entry) member;
                   clean(session, m.getKey(), visitedObjects);
                   clean(session, m.getValue(), visitedObjects);
                   map.put (m.getKey(), m.getValue());
               }
           }

           return (map);
       }

       if (obj instanceof HibernateProxy) {
           clazz = HibernateProxyHelper.getClassWithoutInitializingProxy(obj);
       } else {
           clazz = obj.getClass();
       }

       visitedObjectsInClass = visitedObjects.get(clazz);
       if (visitedObjectsInClass == null) {
           visitedObjectsInClass = new HashMap<Object, Object>();
           visitedObjects.put(clazz, visitedObjectsInClass);
       } else if (visitedObjectsInClass.containsKey(obj)) {
           return visitedObjectsInClass.get(obj);
       }

       newObj = clazz.newInstance();
       visitedObjectsInClass.put(obj, newObj);

       if (!Hibernate.isInitialized(obj)) {
           if (session != null) {
               clazzMetaData =
session.getSessionFactory().getClassMetadata(newObj.getClass());
               Serializable id = clazzMetaData.getIdentifier(obj,
EntityMode.POJO);
               clazzMetaData.setIdentifier(newObj, id, EntityMode.POJO);
           }
       } else {
           descriptors = PropertyUtils.getPropertyDescriptors(newObj);
           length = descriptors.length;
           for (index = 0; index < length; index++) {
               property = descriptors[index].getName();
               if (!property.equals("class")) {
                   try {
                       value = PropertyUtils.getProperty(obj, property);
                       cleanValue = clean(session, value, visitedObjects);
                       PropertyUtils.setProperty(newObj, property, cleanValue);
                   } catch (NoSuchMethodException e) {
                   } catch (Exception e) {
                       if (logger.isInfoEnabled()) logger.info("Error
Clean: " + obj.getClass() + " - " + property + " - " +
value.getClass() + " - " + cleanValue.getClass());
                       throw(e);
                   }
               }
           }
       }

       return (newObj);
   }

   private static Collection createCollection(Collection obj) throws
Exception {
       Collection newObj = null;

       if (obj instanceof SortedSet)
           newObj = new TreeSet ();
       else if (obj instanceof Set)
           newObj = new HashSet ();
       else
           newObj = new ArrayList ();

       return (newObj);
   }

   private static Map createMap(Map obj) throws Exception {
       Map newObj = null;

       if (obj instanceof SortedMap)
           newObj = new TreeMap ();
       else
           newObj = new HashMap ();

       return (newObj);
   }
}

Hope this helps.

Regards,

Néstor Boscán

Alfredo Quiroga-Villamil

unread,
Dec 16, 2011, 12:49:16 PM12/16/11
to google-we...@googlegroups.com
Nestor is right on!

I have heavily used Gilead (http://noon.gilead.free.fr/gilead/index.php?page=gwt) which solves exactly this: 

"The problem with Hibernate and
any framework that serializes POJOs (GWT, WebServices, JSON, etc) is
that Hibernate does not return POJOs, but Proxies of your POJOs. So
basically the solution to this problem is to clean your objects
returned by Hibernate to convert them to POJOs"

In fact, I prefer using gilead any day over RF. This probably sounds controversial I know and I is not intended to spark further comparison between the two approaches. Using gilead I have POJO's under /shared that are really entities and this is all handled transparently by gilead.

Regards,

Alfredo

2011/12/16 Néstor Boscán <nest...@gmail.com>
--
You received this message because you are subscribed to the Google Groups "Google Web Toolkit" group.
To post to this group, send email to google-we...@googlegroups.com.
To unsubscribe from this group, send email to google-web-tool...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/google-web-toolkit?hl=en.




--
Alfredo Quiroga-Villamil

AOL/Yahoo/Gmail/MSN IM:  lawwton


Néstor Boscán

unread,
Dec 16, 2011, 12:50:54 PM12/16/11
to google-we...@googlegroups.com
I have not checked Gilead. I'm checking it.

Ed

unread,
Dec 17, 2011, 5:37:18 AM12/17/11
to google-we...@googlegroups.com
Why not just use the RequestFactory (RF)...:
Currently I  prefer this above other methods like DTO usage or Gilead (= memory monster -> bad scaling).

- Ed

Elhanan

unread,
Dec 26, 2011, 3:07:02 PM12/26/11
to google-we...@googlegroups.com
the ideal of of "one class that rules them all" may sound great at first, especially if your'e coming of plain old server side where you use spring hibernate and what not, and you take for granted that hibernate does behind the scenes. 

but in gwt, you have to be painfully aware of this. the main reason any persistence proxies your pojo's is to track the changes your'e making, no proxies no change tracking, and that's a huge deal, problem as everyone mentioned here that proxies can't leave in the client side, so you can't track changes,.

but using gilead for this thing is not exactly the golden solution, (not that there is one mind you), gilead uses beanlib behind the scenes, and that's a framework for object graph CLONE (it's grand i've workd with it before) ,but you should be aware the cloning a complex graph of objects may cause a problem in the future, as you try to attach the graph to the session for persistence.
this is because hibernate may tend to load objects using the session, that you haven't attached yet, thus making the way for the infamous NonUniqueObjectException.

aside from that, gilead currently only works with hibernate, meaning you can kiss good bye to jpa api's . (sure you wanna do that, think hard before you answer, then think harder) .

now comes RequestFactory, yes, it has it's issues, including annoying amount of boilerplate code, but it does allow you to send and synchronize changes from client to server automatically, on the down side it forces you to create interfaces for the client side. which although smell  like an old sock , it's better now since google were kind enough to include annotation processing in their GPE, (which alarms you of any missing mismatch of getter setter)

another thing with RF, is that google is pushing it really hard, including development on android platform, and using it on the server side as well. so you should take that as a plus also.


karim dounas

unread,
Jul 9, 2013, 9:58:15 AM7/9/13
to google-we...@googlegroups.com, alex.w...@googlemail.com

Another solution is to detach Hibernate proxies manually (easy), then you don't need to use a third part library or to duplicate your objects with DTO.

1. create an interface Detachable {void detach();}

2. in each entity used on client side, remove the lazy loading & add the Detachable implementation :
    public void detach() {
        // replace Hibernate proxies by java objects
        final List<String> lstNames = new ArrayList<String>();
        for (String name : this.names)
            lstNames.add(name);
        this.names = lstNames;
        // do the same on each attribute
        for (EntityChild child : childs)
            child.detach();
    }

3. Make a call to detach() before serialization, you can do it :
- in your code at the end of the method
- in a custom implementation of the Serializator which extends the one used by GWT

Ed

unread,
Jul 9, 2013, 10:26:27 AM7/9/13
to google-we...@googlegroups.com, alex.w...@googlemail.com
+1 For Karim solution.

I use Dozer now, but it costs a lot of time to put it all together, I even had to patch Dozer :(..

So I would go for the manual solution: you have full control, compose one dto that exists of several other domain objects. 
At the end the manual solution costs you almost the same as the "configuration" solution like I did, and don't forget the Reflection overhead that is certainly impacting your performance.

Juan Pablo Gardella

unread,
Jul 9, 2013, 10:30:24 AM7/9/13
to google-we...@googlegroups.com, alex.w...@googlemail.com
I've used "detach solution" using a filter, so it is transparent for you. I put a sample using it: https://groups.google.com/forum/#!topic/google-web-toolkit/fkbowz5-5do

But I prefer using the DTO alternative (I am using Dozzer). As Ed said, perhaps you spent some time in the configuration, but I prefer this alternative instead sending the model. A DTO can adjust better to the UI than an entity. 

Juan


2013/7/9 Ed <post2...@gmail.com>

--
You received this message because you are subscribed to the Google Groups "Google Web Toolkit" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-tool...@googlegroups.com.

To post to this group, send email to google-we...@googlegroups.com.

Timothy Spear

unread,
Jul 9, 2013, 10:39:40 AM7/9/13
to google-we...@googlegroups.com, Timothy Spear, alex.w...@googlemail.com
I looked at Dozzer and ran into to many issues. Since I was going with the command pattern I converted all result sets to a simple <ArrayList<HashMap<String,String>>
For performance reasons, I do cap the amount of data I return. 
Much smaller code base (but does dramatically increase runtime error checking).

Tim

Juan Pablo Gardella

unread,
Jul 9, 2013, 10:42:16 AM7/9/13
to google-we...@googlegroups.com
I don't have issues with Dozzer. What kind of problems dou you have?


2013/7/9 Timothy Spear <n61...@gmail.com>

Timothy Spear

unread,
Jul 9, 2013, 11:36:05 AM7/9/13
to google-we...@googlegroups.com, Timothy Spear
Juan,

All the issues I ran into were probably solvable. But here is the high level:
-- Since I was using the command pattern I initially returned a list of my parent abstract data object which all data objects inherit from. This created a very large code base, and in fact would get random errors in Dev Mode and alternate behavior compared to what was expected when I deployed. 
-- I do stateless sessions, each call to the server is atomic in nature. This is for scaling reasons, I test with multiple servers and will via scripts rotate a single client session to multiple servers to verify I can scale horizontally. I did not look into why, but Dozzer seemed to have issues with this (I would get persistence errors), my assumption it is caching something on the server.
-- Compile time, drove me nuts. I am impatient. :D
-- I have a fair number of database linkages all defined as lazy. Dozzer by default seemed to instantiate them or I ended up with errors when I serialized them for sending to the client.


Tim 

Ed Bras

unread,
Jul 9, 2013, 11:52:09 AM7/9/13
to google-we...@googlegroups.com
When I was using Dozer, I did ran against "many" bugs (about 5 years ago). Just search on my name in de dozer issue tracker ;) I think most of them are solved now. In the mean time I patched it as I couldn't wait till they got fixed. I also contributed some code to Dozer back then, but Dozer had resourcing problems (people getting pregnant/married/starting family, etc... ), I don't know how this is now.
I also added some Dozer optimizations.

One issue I can remember (look in the dozer issue tracker for details and more issues): Hibernate lazy loading. I do a lot of lazy loading with Hibernate, fully tested, to performance optimize Hibernate usage.
However, Dozer did just scan all methods/fields and loaded the data from the db :(..  
So I added some Condition interface, that is injected and asked if the method/field is a valid candidate...
And I remember Dozer could do a better job in crawling recursive code with Lists/Sets involved (creating unnecessary/duplicated Array Lists/Sets, etc..) ... At the end I was almost rebuilding Dozer :(...
And Dozer had problems with super classes/interfaces which I have a lot.

I basically create light weight DTO objects that contains data from several db entities. I strongly follow the Entity/ValueType pattern from Hibernate.



Reply all
Reply to author
Forward
0 new messages