You can write a generic substitute that traverses the entire graph and replaces all proxies for a given entity instance, something like this:
import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import org.hibernate.Hibernate; import org.hibernate.proxy.HibernateProxy; public class HibernateProxyReplacer { @SuppressWarnings("unchecked") public <T extends Entity> T replaceProxies(T entity) { try { return (T) replaceProxies(entity, new ArrayList<Object>()); } catch (Exception e) { throw new RuntimeException(e); } } @SuppressWarnings("unchecked") private Object replaceProxies(Object entity, List<Object> processedObjects) throws Exception { entity = getImplementation(entity); if (isProcessed(entity, processedObjects)) { return entity; } processedObjects.add(entity); for (Field field : getDeclaredFields(entity)) { if (isStaticOrFinal(field)) { continue; } field.setAccessible(true); Object value = field.get(entity); if (value == null) { continue; } Hibernate.initialize(value); if (value instanceof Collection) { replaceProxiesInCollection((Collection<Object>) value, processedObjects); } else if (value instanceof Entity) { field.set(entity, replaceProxies(value, processedObjects)); } } return entity; } private Object getImplementation(Object object) { return object instanceof HibernateProxy ? ((HibernateProxy) object).getHibernateLazyInitializer().getImplementation() : object; } private boolean isStaticOrFinal(Field field) { return ((Modifier.STATIC | Modifier.FINAL) & field.getModifiers()) != 0; } private List<Field> getDeclaredFields(Object object) { List<Field> result = new ArrayList<Field>(Arrays.asList(object.getClass().getDeclaredFields())); for (Class<?> superclass = object.getClass().getSuperclass(); superclass != null; superclass = superclass.getSuperclass()) { result.addAll(Arrays.asList(superclass.getDeclaredFields())); } return result; } private void replaceProxiesInCollection(Collection<Object> collection, List<Object> processedObjects) throws Exception { Collection<Object> deproxiedCollection = new ArrayList<Object>(); for (Object object : collection) { deproxiedCollection.add(replaceProxies(object, processedObjects)); } collection.clear(); collection.addAll(deproxiedCollection); } private boolean isProcessed(Object object, List<Object> processedObjects) { for (Object processedObject : processedObjects) {
Do not forget to cancel the transaction in which this is done (Hibernate might think that the object is dirty, because we manually changed the field values). Or make it read-only (by setting the flash mode to manual). Or, explicitly clear the session without flushing it so that the freed graph is disconnected.
If this is an obstacle to your requirements, you can change this approach by reading the values ββfrom the managed entity instance and setting the deproxed values ββto another instance. Thus, you can create a new separate instance without managed entities, whose entire schedule is initialized without any proxy.
Or you can just save the information about the necessary changes and apply them later in the transaction of a single instance, for example:
commands.add(new ReplaceFieldCommand(field, entity, deproxiedObject)); commands.add(new ReplaceCollectionCommand(collection, entity, deproxiedCollection));
source share