1. 程式人生 > >HibernateProxy異常處理 java.lang.UnsupportedOperationException: Attempted to serialize java.lang.Class:

HibernateProxy異常處理 java.lang.UnsupportedOperationException: Attempted to serialize java.lang.Class:

這裡使用google的Gson包做JSON轉換,因為較早的1.4版本的FieldAttributes類中沒有getDeclaringClass()這個方法,這個方法是獲取field所屬的類,在我的排除策略中會用到。

排除策略 
  最簡單的gson轉換可以是這樣的,但卻沒有多少實際的作用。切面日誌時,一個實體和其他實體存在關聯,這時候就需要通過自定義排除策略決定如何轉換關聯物件,否則可能出現“爆炸式”的json字串。 

複製程式碼
Gson gson = new Gson();
int[] ints = {1, 2, 3, 4, 5};
String[] strings = {"abc", "def", "ghi"};

// Serialization gson.toJson(ints); ==> prints [1,2,3,4,5] gson.toJson(strings); ==> prints ["abc", "def", "ghi"]
複製程式碼

下面是我定義的一個排除策略的類,能基本滿足需求,從內網搬過來的,未測試 

複製程式碼
package com.lingceng.magic.logutil;

import org.apache.commons.lang.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.ExclusionStrategy; import com.google.gson.FieldAttributes; public class TargetStrategy implements ExclusionStrategy { private static Logger log = LoggerFactory.getLogger(TargetStrategy.class); private Class<?> target; private String[] fields; private Class<?>[] clazz;
private boolean reverse; public TargetStrategy(Class<?> target) { super(); this.target = target; } @Override public boolean shouldSkipClass(Class<?> class1) { return false; } @Override public boolean shouldSkipField(FieldAttributes fieldattributes) { Class<?> owner = fieldattributes.getDeclaringClass(); Class<?> c = fieldattributes.getDeclaredClass(); String f = fieldattributes.getName(); boolean isSkip = false; if (owner == target) { if (ArrayUtils.contains(fields, f)) { log.debug("fitler field:{} for class:{}", f, owner); isSkip = true; } if (ArrayUtils.contains(clazz, c)) { log.debug("fitler class:{} for class:{}", c, owner); isSkip = true; } if (reverse) { isSkip = !isSkip; } } return isSkip; } public void setFields(String[] fields) { this.fields = fields; } public void setClazz(Class<?>[] clazz) { this.clazz = clazz; } public void setReverse(boolean reverse) { this.reverse = reverse; } }
複製程式碼

使用的時候是這樣的 

複製程式碼
TargetStrategy ts = new TargetStrategy(Student.class);
//這裡表示僅轉換Student中的id和name屬性
ts.setFields(new String[] {"id", "name"});
ts.setReverse(true);

Gson gson = new GsonBuilder().setExcludeStrategy(ts).create();
gson.toJson(teacher);
複製程式碼

HibernateProxy異常處理 
  在使用Hibernate時,那麼很可能遇到這樣的錯誤: 
java.lang.UnsupportedOperationException: Attempted to serialize java.lang.Class: org.hibernate.proxy.HibernateProxy. Forgot to register a type adapter? 
  因為gson在轉換時是使用的反射機制,當獲取的實體物件還在hibernate代理的時候,例如剛通過Id獲取到,這時候獲取到的便是代理物件HibernateProxy。這和直接呼叫實體物件的get方法不同,獲取物件的屬性就不能起作用。 
  解決的方法便是將代理物件例項化,見下面的程式碼 

複製程式碼
/**
 * This TypeAdapter unproxies Hibernate proxied objects, and serializes them
 * through the registered (or default) TypeAdapter of the base class.
 */
public class HibernateProxyTypeAdapter extends TypeAdapter<HibernateProxy> {

    public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
        @Override
        @SuppressWarnings("unchecked")
        public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
            return (HibernateProxy.class.isAssignableFrom(type.getRawType()) ? (TypeAdapter<T>) new HibernateProxyTypeAdapter(gson) : null);
        }
    };
    private final Gson context;

    private HibernateProxyTypeAdapter(Gson context) {
        this.context = context;
    }

    @Override
    public HibernateProxy read(JsonReader in) throws IOException {
        throw new UnsupportedOperationException("Not supported");
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    @Override
    public void write(JsonWriter out, HibernateProxy value) throws IOException {
        if (value == null) {
            out.nullValue();
            return;
        }
        // Retrieve the original (not proxy) class
        Class<?> baseType = Hibernate.getClass(value);
        // Get the TypeAdapter of the original class, to delegate the serialization
        TypeAdapter delegate = context.getAdapter(TypeToken.get(baseType));
        // Get a filled instance of the original class
        Object unproxiedValue = ((HibernateProxy) value).getHibernateLazyInitializer()
                .getImplementation();
        // Serialize the value
        delegate.write(out, unproxiedValue);
    }
}
複製程式碼

使用的時候將該TypeAdapter的Factory註冊到GsonBuilder,上面的程式碼變為 

Gson gson = new GsonBuilder().setExcludeStrategy(ts)
.registerTypeAdapterFactory(HibernateProxyTypeAdapter.FACTORY)
.create();
gson.toJson(teacher);