[jboss-user] [EJB 3.0] - Re: Detach entities - Obtaining a clear pojo

fatbatman do-not-reply at jboss.com
Thu Sep 7 07:05:19 EDT 2006


anonymous wrote : Which got me to thinking.  Why, if we're using EJB3 to manage our POJO Entities, it is acceptable to return an object with Hibernate classes to the client in the first place?  JBoss itself is requiring EJB3 clients to know Hibernate's involved?!  Shouldn't the container be removing all of that stuff for us once the object leaves the session/transaction?  Then Hibernate would be completely transparent as the storage mechanism for CMP, and the application wouldn't need to know anything about it.

I agree that was one of my main points - Is a Pojo really a Pojo if its depends on specific 3rd party classes?  Not in my book its not.


I've removed the @Entity requirement as we don't actually care if the object is an entity or not, its only the presence of PersistentCollection members that we must check for, and we can only do that by checking each field or each object.
I've kept a check to eleminate classes that are definitely not going to contain PersistentCollection fields for performance reasons.

This new implementation allows us to recurse any object graph looking for PersistentCollection fields that might be hidden within it, as long as they are accessible via a get* method.

package org.hibernate.collection;
  | 
  | import java.lang.reflect.InvocationTargetException;
  | import java.util.ArrayList;
  | import java.util.Collection;
  | import java.util.Map;
  | 
  | import org.apache.commons.beanutils.PropertyUtils;
  | import org.hibernate.LazyInitializationException;
  | 
  | /**
  |  * Some utils methods to get a complete clean POJO from an entity bean with hibernate specific 
  |  * fields stripped out. Note this class is not meant as a way of avoid LazyInitializationException's.  
  |  * Its purpose to strip out data before sending the object to a remote client so that does not have 
  |  * knowledge of hibernate classes. 
  |  * @author ALR 5/9/2006
  |  * @author modified by James Adams 6/9/2006, 7/9/2006
  |  *
  |  */
  | public class HibernateCleaner {
  | 
  | 	/**
  | 	 * Traverse through the object removing all fields of type org.hibernate.collection.PersistentCollection.
  | 	 * by following all bean style get* methods.
  | 	 * *Note* the object cannot be reattached after calling this method.
  | 	 * 
  | 	 * @param obj
  | 	 * @return obj with no PersistentCollection objects within it.
  | 	 */
  | 	public static void clean(Object obj){		
  | 		try {
  | 			removePersistenceContext(obj, new ArrayList<Integer>());
  | 		} catch (IllegalAccessException e) {
  | 			throw new RuntimeException(e);
  | 		} catch (InvocationTargetException e) {
  | 			throw new RuntimeException(e);
  | 		} catch (NoSuchMethodException e) {
  | 			throw new RuntimeException(e);
  | 		}
  | 	}
  | 	
  | 	/**
  | 	 * Checks if the object MIGHT contain PersistentCollection fields by checking if its not a primative, or if its in package
  | 	 * java.* or javax.*.  **NOTE** - This relies on there not being an entity within those packages.
  | 	 * A more conservative aproach is to only check if its a primative.
  | 	 * @param obj
  | 	 * @author James Adams
  | 	 */
  | 	public static boolean couldObjectContainPersistentCollections(Object obj){
  | 		
  | 		if(obj.getClass().isPrimitive()){ return false; }
  | 		
  | 		//TODO - This is not full proof and relys on people not using package with names below.  More conservative approach
  | 		//is to remove this
  | 		if(obj.getClass().getName().startsWith("java.") || obj.getClass().getName().startsWith("javax.")){
  | 			return false;
  | 		}
  | 		
  | 		return true;	//If we got here it might be a hiberate persisted object
  | 	}
  | 	
  | 	/**
  | 	 * If the specified object's identity hash code is not in the specified
  | 	 * collection of visited hash codes, removes of all data binding the object
  | 	 * (and its members) to a specific persistence context, leaving intact only
  | 	 * model-centric data
  | 	 * 
  | 	 * @param visitedObjectHashCodes
  | 	 * @param obj
  | 	 * @author ALR
  | 	 * @author James Adams, modified 6/9/2006,7/9/2006
  | 	 * @throws NoSuchMethodException 
  | 	 * @throws InvocationTargetException 
  | 	 * @throws IllegalAccessException 
  | 	 */
  | 	private static Object removePersistenceContext(Object obj, Collection<Integer> visitedObjectHashCodes) 
  | 		throws IllegalAccessException, InvocationTargetException, NoSuchMethodException{
  | 		if(obj==null){
  | 			return null;
  | 		}
  | 
  | 		if(visitedObjectHashCodes.contains(System.identityHashCode(obj))){
  | 			return obj;
  | 		}
  | 		
  | 		//Add the object's hash to the Collection of visited hash codes
  | 		visitedObjectHashCodes.add(System.identityHashCode(obj));
  | 
  | 		try{			
  | 			// If runtime type of persistentCollection is PersistentCollection we get the inner collection
  | 			//from within the hibernate proxy
  | 			if ((obj instanceof PersistentCollection)){				
  | 				obj = getInnerCollection((PersistentCollection)obj);
  | 			}
  | 		}catch(LazyInitializationException e){
  | 			return null;		
  | 		}
  | 		
  | 		if(obj==null){
  | 			return null;
  | 		}
  | 		
  | 		if(obj instanceof Collection){		
  | 			for(Object o : (Collection)obj){
  | 				//recurse into each element of the collection.  We don't need to set the value 
  | 				//of the object in the Collection as the reference will be adjusted, doing so within this 
  | 				//iterable loop would result in a ConcurrentModificationException anyway. 
  | 				removePersistenceContext(o, visitedObjectHashCodes);
  | 				//TODO - Does Hibernate support Collections of primatives? As this will not work when the 
  | 				//Collection is of types that Java does not pass by reference; primatives, String, Integer, 
  | 				//Float etc.  In this case we need to create a new Collection and populate it. 
  | 			}
  | 		}else if(!couldObjectContainPersistentCollections(obj) ){  	//we don't need to investigate all fields if its not an entity
  | 			return obj;
  | 		}else{	
  | 			Map allMembers = PropertyUtils.describe(obj);
  | 			for(Object member : allMembers.entrySet()){
  | 				Map.Entry m = (Map.Entry)member;
  | 				try {
  | 					try{
  | 						PropertyUtils.setProperty(obj, m.getKey().toString(), removePersistenceContext(m.getValue(), visitedObjectHashCodes));			
  | 					}catch (LazyInitializationException e){
  | 						PropertyUtils.setProperty(obj, m.getKey().toString(), null);
  | 					}
  | 				} catch (IllegalAccessException e) {
  | 					throw new RuntimeException(e);
  | 				} catch (InvocationTargetException e) {
  | 					throw new RuntimeException(e);
  | 				} catch (NoSuchMethodException e) {				
  | 				}
  | 			}
  | 		}
  | 		return obj;
  | 	}
  | 	
  | 	/**
  | 	 * Because this class is in package org.hibernate.collection this method
  | 	 * allows us to access the inner protected fields of the hibernate collections
  | 	 * @param obj
  | 	 * @return The inner collection object of the PersistentCollection parameter
  | 	 * @author James Adams
  | 	 */
  | 	protected static Object getInnerCollection(PersistentCollection obj){
  | 		if(obj instanceof PersistentBag){
  | 			return ((PersistentBag)obj).bag;
  | 		}else if(obj instanceof PersistentList){
  | 			return ((PersistentList)obj).list;			
  | 		}else if(obj instanceof PersistentSet){
  | 			return ((PersistentSet)obj).set;			
  | 		}else if(obj instanceof PersistentMap){
  | 			return ((PersistentMap)obj).map;			
  | 		}else if(obj instanceof PersistentSortedMap){
  | 			return ((PersistentSortedMap)obj).map;			
  | 		}else if(obj instanceof PersistentSortedSet){
  | 			return ((PersistentSortedSet)obj).set;			
  | 		}else{
  | 			return null;
  | 		}		
  | 	}	
  | 	
  | }
  | 

View the original post : http://www.jboss.com/index.html?module=bb&op=viewtopic&p=3970058#3970058

Reply to the post : http://www.jboss.com/index.html?module=bb&op=posting&mode=reply&p=3970058



More information about the jboss-user mailing list