扩展hibernate用自己的集合类

来源:互联网 发布:刀锋1937 知乎 编辑:程序博客网 时间:2024/06/14 02:17

ibernate在使用关联集合的时候有自己的几种类型,分别是set,list,map,bag等,而对应的hibernate实现是 PersistentSet, PersistentList, PersistentBag等,几种集合类型的使用场合问题并不是今天要讨论的话题,今天要讨论的是如何在程序中使用我们自己写的集合类型,这一点
当然很多人人会质疑这样做的必要性,他们会问hibernate提供的集合类型已经够用了,为什么还要自己扩展呢? 事实上在有些情况下使用自己的集合类型是非常重要的,比如说(下面我们就以PersistentSet类举例,其他集合类情况类似)

PersistentSet类并没有序列化id,也就是说在分布式环境中如果两边的jvm版本不一样,那么没有序列化id的话,序列化一方会采用自己默认的序列化id,而反序列化一方的也会采用自己的默认的序列化id,而这两个id一般是不一样的,导致一方序列化之后另一方就无法进行正常的反序列化。当然我想应该还有其他情况需要这种扩展的,所以把它共享出来。

考虑到直接修改源代码可能和开源协议会有冲突,所以我就想到扩展自己的集合类,但是在互联网上并没有相关的信息,我在阅读了hibernate3.1的源代码之后找到了解决方案。

以下是具体实现和我的解决问题的过程:

当时想到思路之后没有办法入手,看了一下文档发现文档中并没有详细的说明,只有在一个不起眼的地方表明set节点有collectiontype这个 attribute,也没有说这个collectiontype是做什么用的。报着试试的心理,我觉得应该从这个collectiontype入手,于是想到要实现自己的集合类就应该先看看hibernate源代码中PersistentSet相关代码,阅读后发现,hibernate返回什么集合类型是由对应的type类决定的,拿SetType来说

代码
  1. public class SetType extends CollectionType {  
  2.   
  3.     public SetType(String role, String propertyRef, boolean isEmbeddedInXML) {  
  4.         super(role, propertyRef, isEmbeddedInXML);  
  5.     }  
  6.   
  7.     public PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister, Serializable key) {  
  8.         if ( session.getEntityMode()==EntityMode.DOM4J ) {  
  9.             return new PersistentElementHolder(session, persister, key);  
  10.         }  
  11.         else {  
  12.             return new PersistentSet(session);  
  13.         }//type类决定返回的集合类  
  14.     }  
  15.   
  16.     public Class getReturnedClass() {  
  17.         return java.util.Set.class;  
  18.     }  
  19.   
  20.     public PersistentCollection wrap(SessionImplementor session, Object collection) {  
  21.         if ( session.getEntityMode()==EntityMode.DOM4J ) {  
  22.             return new PersistentElementHolder( session, (Element) collection );  
  23.         }  
  24.         else {  
  25.             return new PersistentSet( session, (java.util.Set) collection );//type类决定返回的集合类  
  26.         }  
  27.     }  
  28.   
  29.     public Object instantiate() {  
  30.         //TODO: Might need to be a LinkedHashSet!!!!!!  
  31.         return new HashSet();  
  32.     }  
  33.       
  34. }  

由此可以看出要返回什么集合类型确实是由对应的type类决定的,那么就是说要实现自己的集合类必须要搞清楚collectiontype的用法,也就是说必须要先处理我自己的collectiontype,而不是我自己的PersistentSet类,既然是type类,那我想就应该查看 org.hibernate.type这个package里的类,看着看着先找到的是一个CustomCollectionType类:

代码
  1. public class CustomCollectionType extends CollectionType {  
  2.   
  3.     private final UserCollectionType userType;  
  4.   
  5.     public CustomCollectionType(Class userTypeClass, String role, String foreignKeyPropertyName, boolean isEmbeddedInXML) {  
  6.         super(role, foreignKeyPropertyName, isEmbeddedInXML);  
  7.           
  8.         if ( !UserCollectionType.class.isAssignableFrom(userTypeClass) ) {  
  9.             throw new MappingException( "Custom type does not implement UserCollectionType: " + userTypeClass.getName() );  
  10.         }//也就是说要实现自己的type类就要实现usercollectiontype这个接口,看到这里,思路基本上就确定了。  
  11.           
  12.         try {  
  13.             userType = (UserCollectionType) userTypeClass.newInstance();  
  14.         }  
  15.         catch (InstantiationException ie) {  
  16.             throw new MappingException( "Cannot instantiate custom type: " + userTypeClass.getName() );  
  17.         }  
  18.         catch (IllegalAccessException iae) {  
  19.             throw new MappingException( "IllegalAccessException trying to instantiate custom type: " + userTypeClass.getName() );  
  20.         }  
  21.   
  22.     }  
  23.       
  24.     public PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister, Serializable key)   
  25.     throws HibernateException {  
  26.         return userType.instantiate(session, persister);  
  27.     }  
  28.   
  29.     public PersistentCollection wrap(SessionImplementor session, Object collection) {  
  30.         return userType.wrap(session, collection);  
  31.     }  
  32.   
  33.     public Class getReturnedClass() {  
  34.         return userType.instantiate().getClass();  
  35.     }  
  36.   
  37.     public Object instantiate() {  
  38.         return userType.instantiate();  
  39.     }  
  40.   
  41.     public Iterator getElementsIterator(Object collection) {  
  42.         return userType.getElementsIterator(collection);  
  43.     }  
  44.     public boolean contains(Object collection, Object entity, SessionImplementor session) {  
  45.         return userType.contains(collection, entity);  
  46.     }  
  47.     public Object indexOf(Object collection, Object entity) {  
  48.         return userType.indexOf(collection, entity);  
  49.     }  
  50.       
  51.     public Object replaceElements(Object original, Object target, Object owner, Map copyCache, SessionImplementor session)  
  52.     throws HibernateException {  
  53.         CollectionPersister cp = session.getFactory().getCollectionPersister( getRole() );  
  54.         return userType.replaceElements(original, target, cp, owner, copyCache, session);  
  55.     }  
  56. }  

发现要实现自己collectiontype必须要实现UserCollectionType这个接口(以上hibernate源代码中抛出的异常告诉我们实现自己的collectiontype必须要实现这个接口),
下面的就是UserCollectionType这个接口,我们只需要模范SetType来实现这个接口就可以了

代码
  1. /**  
  2.  * A custom type for mapping user-written classes that implement <tt>PersistentCollection</tt>  
  3.  *   
  4.  * @see org.hibernate.collection.PersistentCollection  
  5.  * @author Gavin King  
  6.  */  
  7. public interface UserCollectionType {  
  8.       
  9.     /**  
  10.      * Instantiate an uninitialized instance of the collection wrapper  
  11.      */  
  12.     public PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister)   
  13.     throws HibernateException;  
  14.       
  15.     /**  
  16.      * Wrap an instance of a collection  
  17.      */  
  18.     public PersistentCollection wrap(SessionImplementor session, Object collection);  
  19.       
  20.     /**  
  21.      * Return an iterator over the elements of this collection - the passed collection  
  22.      * instance may or may not be a wrapper  
  23.      */  
  24.     public Iterator getElementsIterator(Object collection);  
  25.   
  26.     /**  
  27.      * Optional operation. Does the collection contain the entity instance?  
  28.      */  
  29.     public boolean contains(Object collection, Object entity);  
  30.     /**  
  31.      * Optional operation. Return the index of the entity in the collection.  
  32.      */  
  33.     public Object indexOf(Object collection, Object entity);  
  34.       
  35.     /**  
  36.      * Replace the elements of a collection with the elements of another collection  
  37.      */  
  38.     public Object replaceElements(  
  39.             Object original,   
  40.             Object target,   
  41.             CollectionPersister persister,   
  42.             Object owner,   
  43.             Map copyCache,   
  44.             SessionImplementor session)  
  45.             throws HibernateException;  
  46.       
  47.     /**  
  48.      * Instantiate an empty instance of the "underlying" collection (not a wrapper)  
  49.      */  
  50.     public Object instantiate();  
  51.       
  52. }  
看到这里开始思路就步上了正轨。大家也都知道怎么做了,开始代码部分吧

步骤1:实现自己的type类

代码
  1. /** 
  2.  *  
  3.  * @author 张荣华 
  4.  * 转载请注明出处 
  5.  */  
  6. public class AhuaxuanPersistentSetType implements UserCollectionType {  
  7.   
  8.      
  9.     public PersistentCollection instantiate(SessionImplementor arg0,  
  10.                                             CollectionPersister arg1) throws HibernateException {  
  11.         return new AhuaxuanPersistentSet(arg0);  
  12.     }//这个方法也必须返回我们自己的集合类  
  13.   
  14.       
  15.     public PersistentCollection wrap(SessionImplementor arg0, Object arg1) {  
  16.         if (arg0.getEntityMode() == EntityMode.DOM4J) {  
  17.             return new PersistentElementHolder(arg0, (Element) arg1);  
  18.         } else {  
  19.             return new AhuaxuanPersistentSet(arg0, (Set) arg1);  
  20.         }//这个方法也必须返回我们自己的集合类  
  21.   
  22. }//这个方法是最重要的,告诉hibernate我们是返回什么样的具体类型,这里我们指定的是自己的集合类,之所以这样写是因为hibernate自己的type类是这样实现的  
  23.   
  24. //而其他需要实现的方法是拷自hibernate自己的type类,基本可以忽略  
  25.   
  26.    }  

步骤2:创建自己的集合类:

代码
  1. public class AhuaxuanPersistentSet extends PersistentSet {  
  2. //这个类其实是继承自hibernate自己的PersistentSet,因为我们只需要下面这个//serialVersionUID而已  
  3. /** 
  4.  *  
  5.  * @author 张荣华 
  6.  * 转载请注明出处 
  7.  */  
  8.   
  9.     private static final long serialVersionUID = 3821652119009257031L;  
  10.   
  11.     public AhuaxuanPersistentSet(SessionImplementor session) {  
  12.         super(session);  
  13.     }  
  14.   
  15.     public AhuaxuanPersistentSet(SessionImplementor session, Set set) {  
  16.         super(session, set);  
  17.     }  
  18.   
  19.     public AhuaxuanPersistentSet() {  
  20.     }  
  21. }  

步骤3:在set节点的collectiontype的attribute上指定我们的AhuaxuanPersistentSetType类了。

代码
  1. <set collection-type=" AhuaxuanPersistentSetType"  
  2.              name="aa" inverse="true" cascade="save-update" >  
  3.             <key>  
  4.                 <column name="aa" precision="10" scale="0"/>  
  5.             </key>  
  6.             <one-to-many class="test.User"/>  
  7.         </set>  

这样我们就成功的扩展了hibernate,并可以让我们的collectiontype指定返回我们的集合类,以上从遇到问题到产生想法再到实现一共使用了2个小时,只要思路正确,就离问题的解决不远了。

 
原创粉丝点击