badqiu

XPer
随笔 - 46, 文章 - 3, 评论 - 195, 引用 - 0
数据加载中……

使用动态代理解决Hibernate序列化,避免延迟加载问题.

在使用Ajax: Hibernate Entity => json, Flex RemoteObject: Hibernate Entity => ActionScript Object的过程,经常碰到如下问题:

问题:

1.Hibernate碰到延迟加载的属性访问时如果session被关闭则抛出LazyInitializationException

2.Hibernate中的one-to-many等关联关系在序列化时如果没有控制,则将整个数据库都有可能被全部序列化

3.过多的使用DTO/ValueObject解决这个问题.

解决办法:

对Entity对象生成一个动态代理,拦截getXXXX()方法,如果访问的是延迟加载的属性,则return null,而不抛出LazyInitializationException,递归生成属性的代理,只要碰到未延迟加载的属性,而序列化会自动停止.避免将整个Entity序列化传播,导致可能序列化整个数据库的问题.

类似解决方案:

dwr中HibernateConverter,在序列化时如上类似,碰到延迟加载的属性自动停止convert工作.而HibernateBeanSerializer则一劳永逸,无论object => json都可以工作.

 

使用用例:

 

Java代码 复制代码
  1. //role为原始对象   
  2. role = (Role)roleDao.getById( new  Long( 1 ));   
  3. //生成的动态代理proxyRole,访问延迟加载的属性将return null   
  4. Role proxyRole = (Role) new  HibernateBeanSerializer<Role>(role).getProxy();   
  5. assertNotNull(role.getResource());     
  6. assertNull(proxyRole.getResource());  //延迟加载,为null   
  7.   
  8. Hibernate.initialize(role.getResource());  //抓取进来   
  9. assertNotNull(proxyRole.getResource());  //不为null   

  

 

源码.

Java代码 复制代码
  1. package  cn.org.rapid_framework.util;   
  2.   
  3. import  java.lang.reflect.Modifier;   
  4. import  java.util.ArrayList;   
  5. import  java.util.Collection;   
  6. import  java.util.LinkedHashMap;   
  7. import  java.util.LinkedHashSet;   
  8. import  java.util.List;   
  9. import  java.util.Map;   
  10. import  java.util.Set;   
  11.   
  12. import  org.aopalliance.intercept.MethodInterceptor;   
  13. import  org.aopalliance.intercept.MethodInvocation;   
  14. import  org.hibernate.Hibernate;   
  15. import  org.hibernate.collection.PersistentCollection;   
  16. import  org.hibernate.proxy.HibernateProxy;   
  17. import  org.springframework.aop.framework.ProxyFactory;   
  18. import  org.springframework.util.StringUtils;   
  19.   
  20. /**  
  21.  * 用于Hibernate Object 的序列化,访问延迟加载的属性不会抛出LazyInitializationException,而会返回null值.  
  22.  * 使用:  
  23.  * <pre>  
  24.  * Blog proxyBlog = new HibernateBeanSerializer(blog).getProxy();  
  25.  * </pre>  
  26.  * @author badqiu  
  27.  * @param <T>  
  28.  */   
  29. public   class  HibernateBeanSerializer <T> {   
  30.     T proxy =  null ;   
  31.      /**  
  32.      */   
  33.      public  HibernateBeanSerializer(T object,String... excludesProperties) {   
  34.          if (object ==  null ) {   
  35.              this .proxy =  null ;   
  36.         } else  {   
  37.             ProxyFactory pf =  new  ProxyFactory();   
  38.             pf.setTargetClass(object.getClass());   
  39.             pf.setOptimize( true );   
  40.             pf.setTarget(object);   
  41.             pf.setProxyTargetClass( true );   
  42.             pf.setOpaque( true );   
  43.             pf.setExposeProxy( true );   
  44.             pf.setPreFiltered( true );   
  45.             HibernateBeanSerializerAdvice beanSerializerAdvice =  new  HibernateBeanSerializerAdvice();   
  46.             beanSerializerAdvice.setExcludesProperties(excludesProperties);   
  47.             pf.addAdvice(beanSerializerAdvice);   
  48.                
  49.              this .proxy = (T)pf.getProxy();   
  50.         }   
  51.     }   
  52.   
  53.      public  T getProxy(){   
  54.          return   this .proxy;   
  55.     }   
  56.        
  57.      static   private   class  HibernateBeanSerializerAdvice  implements  MethodInterceptor {   
  58.          private  String[] excludesProperties =  new  String[ 0 ];    
  59.          public  String[] getExcludesProperties() {   
  60.              return  excludesProperties;   
  61.         }   
  62.          public   void  setExcludesProperties(String[] excludesProperties) {   
  63.              this .excludesProperties = excludesProperties ==  null  ?  new  String[ 0 ] : excludesProperties;   
  64.         }   
  65.          public  Object invoke(MethodInvocation mi)  throws  Throwable {   
  66.             String propertyName = getPropertyName(mi.getMethod().getName());   
  67.             Class returnType = mi.getMethod().getReturnType();   
  68.                
  69.              if (propertyName ==  null ) {   
  70.                  return  mi.proceed();   
  71.             }   
  72.              if (!Hibernate.isPropertyInitialized(mi.getThis(), propertyName)) {   
  73.                  return   null ;   
  74.             }   
  75.              if (isExclude(mi, propertyName)) {   
  76.                  return   null ;   
  77.             }   
  78.                
  79.             Object returnValue = mi.proceed();   
  80.              return  processReturnValue(returnType, returnValue);   
  81.         }   
  82.            
  83.          private  Object processReturnValue(Class returnType, Object returnValue) {   
  84.              if (returnValue ==  null )   
  85.                  return   null ;   
  86.              if (returnType !=  null  && Modifier.isFinal(returnType.getModifiers())) {   
  87.                  return  returnValue;   
  88.             }   
  89.              //This might be a lazy-collection so we need to double check   
  90.              if (!Hibernate.isInitialized(returnValue)) {   
  91.                  return   null ;                   
  92.             }   
  93.                
  94.              //this is Hibernate Object   
  95.              if (returnValue  instanceof  HibernateProxy) {   
  96.                  return   new  HibernateBeanSerializer(returnValue).getProxy();   
  97.             } else   if (returnValue  instanceof  PersistentCollection) {   
  98.                  if (returnType.isAssignableFrom(Map. class )) {   
  99.                     Map proxyMap =  new  LinkedHashMap();   
  100.                     Map map = (Map)returnValue;   
  101.                     Set<Map.Entry> entrySet = map.entrySet();   
  102.                      for (Map.Entry entry : entrySet) {   
  103.                         proxyMap.put(entry.getKey(),  new  HibernateBeanSerializer(entry.getValue()));   
  104.                     }   
  105.                      return  proxyMap;   
  106.                 }   
  107.                    
  108.                 Collection proxyCollection =  null ;   
  109.                  if (returnType.isAssignableFrom(Set. class )) {   
  110.                     proxyCollection =  new  LinkedHashSet();   
  111.                 } else   if (returnType.isAssignableFrom(List. class )) {   
  112.                     proxyCollection =  new  ArrayList();   
  113.                 } else  {   
  114.                      return  returnValue;   
  115.                 }   
  116.                    
  117.                  for (Object o : (Collection)returnValue) {   
  118.                     proxyCollection.add( new  HibernateBeanSerializer(o).getProxy());   
  119.                 }   
  120.                  return  proxyCollection;   
  121.             } else  {   
  122.                  return  returnValue;   
  123.             }   
  124.         }   
  125.     
  126.          private   boolean  isExclude(MethodInvocation mi, String propertyName)   
  127.                  throws  Throwable {   
  128.                
  129.              for (String excludePropertyName : excludesProperties) {   
  130.                  if (propertyName.equals(excludePropertyName)) {   
  131.                      return   true ;   
  132.                 }   
  133.             }   
  134.                
  135.              return   false ;   
  136.         }   
  137.            
  138.          private   static  String getPropertyName(String methodName) {   
  139.             String propertyName =  null ;   
  140.              if (methodName.startsWith( "get" )) {   
  141.                 propertyName = methodName.substring( "get" .length());   
  142.             } else   if (methodName.startsWith( "is" )) {   
  143.                 propertyName = methodName.substring( "is" .length());   
  144.             } else   if (methodName.startsWith( "set" )) {   
  145.                 propertyName = methodName.substring( "set" .length());   
  146.             }   
  147.              return  propertyName ==  null  ?  null  : StringUtils.uncapitalize(propertyName);   
  148.         }   
  149.     }   
  150. }  

 

 

 

 另这个类属于rapid-framework的一部分,v2.0版本的flex RemoteObject将采用这个办法.preview版本即将发布

posted on 2008-10-31 00:33 badqiu 阅读(3172) 评论(3)  编辑  收藏

评论

# re: 使用动态代理解决Hibernate序列化,避免延迟加载问题.  回复  更多评论   

用了spring,你的方法就不行了吧
2008-10-31 09:12 | lsqlister

# re: 使用动态代理解决Hibernate序列化,避免延迟加载问题.  回复  更多评论   

不用spring么?
有spring用干嘛不用?重复发明轮子!!
不过spring后面也是使用cglib生成动态代理,将以上代码修改,可以改为只依赖cglib的Enhancer
2008-10-31 09:26 | badqiu

# re: 使用动态代理解决Hibernate序列化,避免延迟加载问题.  回复  更多评论   

但这也是一种很有趣的解法。
2008-11-01 17:07 | 金山词霸2008

只有注册用户登录后才能发表评论。


网站导航: