Gostaria de compartilhar um código com a comunidade, que consiste em retirar totalmente um objeto do hibernate, ou seja desvincular o objeto da sessao e falar "Olha cara, agora é um objeto normal sem proxy sem bags sem NADAA!".
Encontrei alguns problemas quando utilizei RMI ou EJB3 ou xml, sendo que quando uma entidade traz uma lista de objetos e ele é do tipo lazy ou mesmo a lista em si vem como "Bag", no momento que o sistema tenta de-serializar ou transformar o objeto em xml, ele vai como bag e não como list ou collection, 0o ... WTF ??
Pois é, e quando do outro lado vc tem um hibernate3.jar ainda passa, mas e quando não tem ?
Ou quando vc não quer que o objeto não acesse os dados do tipo Lazy ??
Ou quando as versoes do hibernate são diferentes e ele não consegue de-serializar o objeto, da aquele errão e vem na cabeça : E AGORA JOSÉ?
Pois é[2], e a primeira solução que vem na cabeça é : vou copiar as informações para um objeto novo e passar esse objeto, pois ele perderá as referencias com o proxy do hibernate -_- funciona ... mas da um trabalho cabuloso!
Por isso, em algumas discussões em foruns chegamos a essa solução :
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Hibernate;
import org.hibernate.proxy.HibernateProxy;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlTransient;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class HibernateDetachUtility {
private static final Log LOG = LogFactory.getLog(HibernateDetachUtility.class);
public static enum SerializationType {
SERIALIZATION, JAXB
}
public static void nullOutUninitializedFields(Object value, SerializationType serializationType) throws Exception {
long start = System.currentTimeMillis();
Set<Integer> checkedObjs = new HashSet<Integer>();
nullOutUninitializedFields(value, checkedObjs, 0, serializationType);
long duration = System.currentTimeMillis() - start;
if (duration > 1000) {
LOG.info("Detached [" + checkedObjs.size() + "] objects in [" + duration + "]ms");
} else {
LOG.debug("Detached [" + checkedObjs.size() + "] objects in [" + duration + "]ms");
}
}
private static void nullOutUninitializedFields(Object value, Set<Integer> nulledObjects, int depth,
SerializationType serializationType) throws Exception {
if (depth > 50) {
LOG.warn("Getting different object hierarchies back from calls: " + value.getClass().getName());
return;
}
if ((value == null) || nulledObjects.contains(System.identityHashCode(value))) {
return;
}
nulledObjects.add(System.identityHashCode(value));
if (value instanceof Object[]) {
Object[] objArray = (Object[]) value;
for(int i = 0; i < objArray.length; i++)
{
nullOutUninitializedFields(objArray[i], nulledObjects, depth +1, serializationType);
}
} else if (value instanceof Collection) {
// Null out any entries in initialized collections
for (Object val : (Collection) value) {
nullOutUninitializedFields(val, nulledObjects, depth + 1, serializationType);
}
} else if (value instanceof Map) {
for (Object key : ((Map)value).keySet()) {
nullOutUninitializedFields(((Map)value).get(key), nulledObjects, depth+1, serializationType);
nullOutUninitializedFields(key, nulledObjects, depth+1, serializationType);
}
}
if (serializationType == SerializationType.JAXB) {
XmlAccessorType at = value.getClass().getAnnotation(XmlAccessorType.class);
if (at != null && at.value() == XmlAccessType.FIELD) {
//System.out.println("----------XML--------- field access");
nullOutFieldsByFieldAccess(value, nulledObjects, depth, serializationType);
} else {
//System.out.println("----------XML--------- accessor access");
nullOutFieldsByAccessors(value, nulledObjects, depth, serializationType);
}
} else if (serializationType == SerializationType.SERIALIZATION) {
// System.out.println("-----------JRMP-------- field access");
nullOutFieldsByFieldAccess(value, nulledObjects, depth, serializationType);
}
}
private static void nullOutFieldsByFieldAccess(Object object, Set<Integer> nulledObjects, int depth,
SerializationType serializationType) throws Exception {
Class tmpClass = object.getClass();
List<Field> fieldsToClean = new ArrayList<Field>();
while (tmpClass != null && tmpClass != Object.class) {
Collections.addAll(fieldsToClean, tmpClass.getDeclaredFields());
tmpClass = tmpClass.getSuperclass();
}
nullOutFieldsByFieldAccess(object, fieldsToClean, nulledObjects, depth, serializationType);
}
@SuppressWarnings("unchecked")
private static void nullOutFieldsByFieldAccess(Object object, List<Field> classFields, Set<Integer> nulledObjects, int depth,
SerializationType serializationType) throws Exception {
boolean accessModifierFlag = false;
for (Field field : classFields) {
accessModifierFlag = false;
if (!field.isAccessible()) {
field.setAccessible(true);
accessModifierFlag = true;
}
Object fieldValue = field.get(object);
if (fieldValue instanceof HibernateProxy) {
Object replacement = null;
if (fieldValue.getClass().getName().contains("javassist")) {
Class assistClass = fieldValue.getClass();
try {
Method m = assistClass.getMethod("writeReplace");
replacement = m.invoke(fieldValue);
String className = fieldValue.getClass().getName();
className = className.substring(0, className.indexOf("_$$_"));
if (!replacement.getClass().getName().contains("hibernate")) {
nullOutUninitializedFields(replacement, nulledObjects, depth+1, serializationType);
field.set(object, replacement);
} else {
replacement = null;
}
} catch (Exception e) {
System.out.println("Unable to write replace object " + fieldValue.getClass());
}
}
if (replacement == null) {
field.set(object, replacement);
/*String className = ((HibernateProxy) fieldValue).getHibernateLazyInitializer().getEntityName();
Class clazz = Class.forName(className);
Class[] constArgs = {Integer.class};
Constructor construct = null;
try {
construct = clazz.getConstructor(constArgs);
replacement = construct.newInstance((Integer) ((HibernateProxy) fieldValue).getHibernateLazyInitializer().getIdentifier());
field.set(object, replacement);
} catch (NoSuchMethodException nsme) {
try {
Field idField = clazz.getDeclaredField("id");
Constructor ct = clazz.getDeclaredConstructor();
ct.setAccessible(true);
replacement = ct.newInstance();
if (!idField.isAccessible()) {
idField.setAccessible(true);
}
idField.set(replacement, (Integer) ((HibernateProxy) fieldValue).getHibernateLazyInitializer().getIdentifier());
} catch (Exception e) {
e.printStackTrace();
System.out.println("No id constructor and unable to set field id for base bean " + className);
}
field.set(object, replacement);
}*/
}
} else {
if (fieldValue instanceof org.hibernate.collection.PersistentCollection) {
// Replace hibernate specific collection types
if (!((org.hibernate.collection.PersistentCollection) fieldValue).wasInitialized()) {
field.set(object, null);
} else {
Object replacement = null;
if (fieldValue instanceof Map) {
replacement = new HashMap((Map) fieldValue);
} else if (fieldValue instanceof List) {
replacement = new ArrayList((List) fieldValue);
} else if (fieldValue instanceof Set) {
replacement = new HashSet((Set) fieldValue);
} else if (fieldValue instanceof Collection) {
replacement = new ArrayList((Collection)fieldValue);
}
setField(object, field.getName(), replacement);
nullOutUninitializedFields(replacement, nulledObjects, depth+1, serializationType);
}
} else {
if (fieldValue != null &&
(fieldValue.getClass().getName().contains("org.rhq") ||
fieldValue instanceof Collection ||
fieldValue instanceof Object[] ||
fieldValue instanceof Map))
nullOutUninitializedFields((fieldValue), nulledObjects, depth+1, serializationType);
}
}
if (accessModifierFlag) {
field.setAccessible(false);
}
}
}
private static void nullOutFieldsByAccessors(Object value, Set<Integer> nulledObjects, int depth,
SerializationType serializationType) throws Exception {
// Null out any collections that aren't loaded
BeanInfo bi = Introspector.getBeanInfo(value.getClass(), Object.class);
PropertyDescriptor[] pds = bi.getPropertyDescriptors();
for (PropertyDescriptor pd : pds) {
Object propertyValue = null;
try {
propertyValue = pd.getReadMethod().invoke(value);
} catch (Throwable lie) {
if (LOG.isDebugEnabled()) {
LOG.debug("Couldn't load: " + pd.getName() + " off of " + value.getClass().getSimpleName(), lie);
}
}
if (!Hibernate.isInitialized(propertyValue)) {
try {
if (LOG.isDebugEnabled()) {
LOG.debug("Nulling out: " + pd.getName() + " off of " + value.getClass().getSimpleName());
}
Method writeMethod = pd.getWriteMethod();
if ((writeMethod != null) && (writeMethod.getAnnotation(XmlTransient.class) == null)) {
pd.getWriteMethod().invoke(value, new Object[] { null });
} else {
nullOutField(value, pd.getName());
}
} catch (Exception lie) {
LOG.debug("Couldn't null out: " + pd.getName() + " off of " + value.getClass().getSimpleName()
+ " trying field access", lie);
nullOutField(value, pd.getName());
}
} else {
if ((propertyValue instanceof Collection)
|| ((propertyValue != null) && propertyValue.getClass().getName().startsWith("org.rhq.core.domain"))) {
nullOutUninitializedFields(propertyValue, nulledObjects, depth + 1, serializationType);
}
}
}
}
private static void setField(Object object, String fieldName, Object newValue) {
try {
Field f = object.getClass().getDeclaredField(fieldName);
if (f != null) {
// try to set the field this way
f.setAccessible(true);
f.set(object, newValue);
}
} catch (NoSuchFieldException e) {
// ignore this
} catch (IllegalAccessException e) {
// ignore this
}
}
private static void nullOutField(Object value, String fieldName) {
try {
Field f = value.getClass().getDeclaredField(fieldName);
if (f != null) {
// try to set the field this way
f.setAccessible(true);
f.set(value, null);
}
} catch (NoSuchFieldException e) {
// ignore this
} catch (IllegalAccessException e) {
// ignore this
}
}
}
Detalhe que eu comentei a parte do ID, pois nao é todo mundo que usa o nome "ID" como chave, portanto caso necessário implementem !
Ele funciona sem a parte do id, pois objetos lazy seriam "buscados" nesse momento, caso não haja a necessidade, deixem comentado pois ele setará NULL nos objetos.
\m/(0.0)\m/
Espero que tenha ajudado pessoal !
Abraços !
Nenhum comentário:
Postar um comentário