天天看點

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);