Hi.
We've been discussing methods to send a managed entity to remote clients over in the JBoss EJB3 forum, and I was hoping for some feedback here on the proposed solutions we've been contemplating. Apologies in advance for the crosspost and lengthy thread. This started:
http://www.jboss.org/index.html?module=bb&op=viewtopic&t=89436
The issue is - because EJB3 Entities are POJOs, I'd like to use as a DTO/VO as well. The only barrier is in the managed collections and the LazyInitializationException that occurs when the remote client attempts to access associations outside the context of the initial transaction/session.
So I made a method that takes a Collection, and ensures that the Impl is in the standard Java Collections API (All code below). Additionally, I added a method to an Entity Base Class (which I call PersistedEntity) which, through reflection, ensures all internal members of the object are not Hibernate Collections, and if they are, replaces 'em.
Questions:
1) Is there a better way to send detached Hibernate objects for use outside of the session?
2) This approach seems to necessitate a Base Entity class from which all entities will extend. Is there a better way to make this a Utility method that accepts an entity of any type and does the same thing?
Code follows.
S,
ALR
PersistenceUtils.java - Utility Class
Code:
/**
* Returns a Collection of all objects in the specified persistentCollection
* without binding to any persistence context or session.
*
* @param <T>
* @param targetCollection
* @param persistentCollection
* @return
*/
public static <T> Collection<T> removeCollectionItemsFromPersistenceContext(
Collection<T> targetCollection, Collection<T> persistentCollection) {
// If runtime type of persistentCollection is not PersistentCollection,
// take no action
if (!(persistentCollection instanceof PersistentCollection))
return persistentCollection;
// Clear existing target
targetCollection.clear();
// Place all items in persistent collection into target
for (T item : persistentCollection) {
targetCollection.add(item);
}
// Return target
return targetCollection;
}
PersistedEntity.java - Base Entity Class
Code:
/**
* Strips the entity of all data binding it to a specific persistence
* context, leaving intact only model-centric data
*
* @author ALR
*/
public void removePersistenceContext() {
// Initialize
Collection<Integer> visitedObjectHashCodes = new ArrayList<Integer>();
// Run
this.removePersistenceContext(this, visitedObjectHashCodes);
}
/**
* 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
*/
private Object removePersistenceContext(Object obj,
Collection<Integer> visitedObjectHashCodes) {
// Ensure the current object has not been visited
if (visitedObjectHashCodes.contains(System.identityHashCode(obj))) {
return obj;
}
// Add the current object's hash to the Collection of visited hash codes
visitedObjectHashCodes.add(System.identityHashCode(obj));
// If the current object is of type Collection
if (Collection.class.isInstance(obj)) {
// Remove persistence context
obj = PersistenceUtils.removeCollectionItemsFromPersistenceContext(
new ArrayList(), (Collection) obj);
}
// Only traverse into types of PersistedEntity
if (!(PersistedEntity.class.isInstance(obj))) {
return obj;
}
// Remove persistence context of all members
Map allMembers = this.getInternalMembers();
for (Object member : allMembers.entrySet()) {
Map.Entry m = (Map.Entry) member;
try {
PropertyUtils.setProperty((Object) obj, m.getKey().toString(),
this.removePersistenceContext((m.getValue()),
visitedObjectHashCodes));
} catch (IllegalAccessException e) {
throw new ApplicationException(e);
} catch (InvocationTargetException e) {
throw new ApplicationException(e);
} catch (NoSuchMethodException e) {
}
}
// Return
return obj;
}
// Internal Helper Methods
/**
* Returns a Map of all internal members of the specified object
*
* @param obj
* The object for which to obtain internal members
*
* @author ALR
* @see http://jakarta.apache.org/commons/beanutils/
*/
protected Map getInternalMembers(PersistedEntity obj) {
try {
// Return a map of all visible properties
return PropertyUtils.describe(obj);
}
// Throw as unchecked exceptions up the stack trace
catch (IllegalAccessException iae) {
throw new ApplicationException(iae);
} catch (NoSuchMethodException nsme) {
throw new ApplicationException(nsme);
} catch (InvocationTargetException ite) {
throw new ApplicationException(ite);
}
}
/**
* Returns a Map of all internal members of this PersistedEntitys
*
* @author ALR
* @see http://jakarta.apache.org/commons/beanutils/
*/
protected Map getInternalMembers() {
return getInternalMembers(this);
}