HibernateProxy異常處理 java.lang.UnsupportedOperationException: Attempted to serialize java.lang.Class:
阿新 • • 發佈:2019-01-07
這裡使用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);