使用AOP实现类型安全的泛型DAO

来源:百度文库 编辑:神马文学网 时间:2024/03/28 19:17:15
由于要求在项目中使用泛型的DAO,所以上网Google了一下,找到了IBM的一篇文章。文章讲得不错,但是有些地方不清楚,如果完全按照那篇文章可能还会遇到一些困难。所以写了这篇文章,解释如何在项目中加入泛型的DAO实现。
首先是总的类关系的UML图:

然后是在配置文件中的关系图:

其中,IStaffDao是我们自己定义的接口,这个接口类似:
public   interface  IStaffDAO  extends  GenericDao < Staff, Integer >{

public  List listAll();

public  Staff getByLogonAndId(String logon, Integer id);

// more 

}


GenericDao 是泛型的 Dao 接口:
/** */ /**
 * 2006-11-22
 * 范型DAO接口
 *  @author  Zou Ang
 * Contact Zou Ang
  */
public   interface  GenericDao < T, PK  extends  Serializable >  {

     /** */ /**
     * 保存一个对象到数据库
     *  @param  newInstance 需要保存的对象
     *  @return
      */
    PK create(T newInstance);
     /** */ /**
     * 从数据库读取一个对象
     *  @param  id 主键
     *  @return
      */
    T read(PK id);

     /** */ /**
     * 更新一个对象
     *  @param  transientObject 被更新的对象
      */
     void  update(T transientObject);

     /** */ /**
     * 删除一个对象
     *  @param  transientObject 被删除的对象
      */
     void  delete(T transientObject);
}

 
GenericDaoHibernateImpl 是 GenericDao 接口的泛型实现 :
 

/** */ /**
 * 2006-11-22
 * 范型DAO实现
 *  @author  Zou Ang
 * Contact Zou Ang
  */
public   class  GenericDaoHibernateImpl < T,PK  extends  Serializable >
     extends  HibernateDaoSupport
         implements  GenericDao < T, PK >  ,FinderExecutor{

     private  Class < T >  type;
     private  FinderNamingStrategy namingStrategy  =   new  SimpleFinderNamingStrategy();  //  Default. Can override in config
     private  FinderArgumentTypeFactory argumentTypeFactory  =   new  SimpleFinderArgumentTypeFactory();  //  Default. Can override in config

     public  GenericDaoHibernateImpl(Class < T >  type){
         this .type  =  type;
    }

     /**/ /*  (non-Javadoc)
     * @see com.gdnfha.atcs.common.service.dao.GenericDao#create(java.lang.Object)
      */
     public  PK create(T newInstance) {
         return  (PK)getHibernateTemplate().save(newInstance);
    }

     /**/ /*  (non-Javadoc)
     * @see com.gdnfha.atcs.common.service.dao.GenericDao#delete(java.lang.Object)
      */
     public   void  delete(T transientObject) {
        getHibernateTemplate().delete(transientObject);
    }

     /**/ /*  (non-Javadoc)
     * @see com.gdnfha.atcs.common.service.dao.GenericDao#read(java.io.Serializable)
      */
     public  T read(PK id) {
         return  (T)getHibernateTemplate().get(type, id);
    }

     /**/ /*  (non-Javadoc)
     * @see com.gdnfha.atcs.common.service.dao.GenericDao#update(java.lang.Object)
      */
     public   void  update(T transientObject) {
        getHibernateTemplate().update(transientObject);
    }

     public  List < T >  executeFinder(Method method,  final  Object[] queryArgs)
    {
         final  Query namedQuery  =  prepareQuery(method, queryArgs);
         return  (List < T > ) namedQuery.list();
    }

     public  Iterator < T >  iterateFinder(Method method,  final  Object[] queryArgs)
    {
         final  Query namedQuery  =  prepareQuery(method, queryArgs);
         return  (Iterator < T > ) namedQuery.iterate();
    }

     private  Query prepareQuery(Method method, Object[] queryArgs)
    {
         final  String queryName  =  getNamingStrategy().queryNameFromMethod(type, method);
         final  Query namedQuery  =  getSession().getNamedQuery(queryName);
        String[] namedParameters  =  namedQuery.getNamedParameters();
         if (namedParameters.length == 0 )
        {
            setPositionalParams(queryArgs, namedQuery);
        }   else  {
            setNamedParams(namedParameters, queryArgs, namedQuery);
        }
         return  namedQuery;
    }

     private   void  setPositionalParams(Object[] queryArgs, Query namedQuery)
    {
         //  Set parameter. Use custom Hibernate Type if necessary
         if (queryArgs != null )
        {
             for ( int  i  =   0 ; i  <  queryArgs.length; i ++ )
            {
                Object arg  =  queryArgs[i];
                Type argType  =  getArgumentTypeFactory().getArgumentType(arg);
                 if (argType  !=   null )
                {
                    namedQuery.setParameter(i, arg, argType);
                }
                 else
                {
                    namedQuery.setParameter(i, arg);
                }
            }
        }
    }

     private   void  setNamedParams(String[] namedParameters, Object[] queryArgs, Query namedQuery)
    {
         //  Set parameter. Use custom Hibernate Type if necessary
         if (queryArgs != null )
        {
             for ( int  i  =   0 ; i  <  queryArgs.length; i ++ )
            {
                Object arg  =  queryArgs[i];
                Type argType  =  getArgumentTypeFactory().getArgumentType(arg);
                 if (argType  !=   null )
                {
                    namedQuery.setParameter(namedParameters[i], arg, argType);
                }
                 else
                {
                     if (arg  instanceof  Collection) {
                        namedQuery.setParameterList(namedParameters[i], (Collection) arg);
                    }
                     else
                    {
                        namedQuery.setParameter(namedParameters[i], arg);
                    }
                }
            }
        }
    }

     public  FinderNamingStrategy getNamingStrategy()
    {
         return  namingStrategy;
    }

     public   void  setNamingStrategy(FinderNamingStrategy namingStrategy)
    {
         this .namingStrategy  =  namingStrategy;
    }

     public  FinderArgumentTypeFactory getArgumentTypeFactory()
    {
         return  argumentTypeFactory;
    }

     public   void  setArgumentTypeFactory(FinderArgumentTypeFactory argumentTypeFactory)
    {
         this .argumentTypeFactory  =  argumentTypeFactory;
    }

}
FinderNamingStrategy 是查找方法的命名规范:
 
public   interface  FinderNamingStrategy
{
     public  String queryNameFromMethod(Class findTargetType, Method finderMethod);
}

目前有两个命名查找策略,使用的是
Simple 的,也就是直接是 < 类型名 >.< 方法名 > 的形式。
public   class  SimpleFinderNamingStrategy  implements  FinderNamingStrategy
{
     public  String queryNameFromMethod(Class findTargetType, Method finderMethod)
    {
         return  findTargetType.getSimpleName()  +   " . "   +  finderMethod.getName();
    }
}

 
FinderArgumentTypeFactory 目前还没有什么作用,主要是返回自定义的 Hibernate 类型:
public   class  SimpleFinderArgumentTypeFactory  implements  FinderArgumentTypeFactory
{
     public  Type getArgumentType(Object arg)
    {
//         if(arg instanceof Enum)
//         {
//             return getEnumType(arg.getClass());
//         }
//         else
//         {
             return   null ;
//         }
    }

//     private Type getEnumType(Class argClass)
//     {
//         Properties p = new Properties();
//         p.setProperty("enumClassName", argClass.getName());
//         Type enumType = TypeFactory.heuristicType("org.hibernate.demo.EnumUserType", p);
//         return enumType;
//     }
}
FinderIntroductionAdvisor 和 FinderIntroductionInterceptor:
public   class  FinderIntroductionAdvisor  extends  DefaultIntroductionAdvisor
{
     public  FinderIntroductionAdvisor()
    {
         super ( new  FinderIntroductionInterceptor());
    }
}

public   class  FinderIntroductionInterceptor  implements  IntroductionInterceptor
{

     public  Object invoke(MethodInvocation methodInvocation)  throws  Throwable
    {

        FinderExecutor executor  =  (FinderExecutor) methodInvocation.getThis();

        String methodName  =  methodInvocation.getMethod().getName();
         if (methodName.startsWith( " get " )  ||  methodName.startsWith( " list " ))
        {
            Object[] arguments  =  methodInvocation.getArguments();
             return  executor.executeFinder(methodInvocation.getMethod(), arguments);
        }
         else   if (methodName.startsWith( " iterate " ))
        {
            Object[] arguments  =  methodInvocation.getArguments();
             return  executor.iterateFinder(methodInvocation.getMethod(), arguments);
        }
//         else if(methodName.startsWith("scroll"))
//         {
//             Object[] arguments = methodInvocation.getArguments();
//             return executor.scrollFinder(methodInvocation.getMethod(), arguments);
//         }
         else
        {
             return  methodInvocation.proceed();
        }
    }

     public   boolean  implementsInterface(Class intf)
    {
         return  intf.isInterface()  &&  FinderExecutor. class .isAssignableFrom(intf);
    }
}

然后就到了配置文件了:
 
      
      <  bean   id  ="abstractDaoTarget"
        class  ="com.gdnfha.atcs.common.service.dao.hibernate.GenericDaoHibernateImpl"
        abstract  ="true"   >
          <  property   name  ="sessionFactory"   >
              <  ref   local  ="sessionFactory"     />
         
          <  property   name  ="namingStrategy"   >
              <  ref   bean  ="simpleFinderNamingStratrgy"     />
         
     

      <  bean   id  ="abstractDao"
        class  ="org.springframework.aop.framework.ProxyFactoryBean"
        abstract  ="true"   >
          <  property   name  ="interceptorNames"   >
              <  list  >
                  <  value  >  finderIntroductionAdvisor 
             
         
     

      <  bean   id  ="finderIntroductionAdvisor"
        class  ="com.gdnfha.atcs.common.service.dao.finder.FinderIntroductionAdvisor"     />

      <  bean   id  ="namingStrategy"
        class  ="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"   >
          <  property   name  ="staticField"   >
              <  value  >  org.hibernate.cfg.ImprovedNamingStrategy.INSTANCE 
         
     

      <  bean   id  ="extendedFinderNamingStrategy"
        class  ="com.gdnfha.atcs.common.service.dao.finder.impl.ExtendedFinderNamingStrategy"     />

      <  bean   id  ="simpleFinderNamingStratrgy"   class  ="com.gdnfha.atcs.common.service.dao.finder.impl.SimpleFinderNamingStrategy"   />
      

     

      <  bean   id  ="staffDao"   parent  ="abstractDao"   >
          <  property   name  ="proxyInterfaces"   >
              <  value  >  com.gdnfha.atcs.maintain.service.dao.IStaffDAO     
         
          <  property   name  ="target"   >
              <  bean   parent  ="abstractDaoTarget"   >
                  <  constructor-arg  >
                      <  value  >  com.gdnfha.atcs.common.pojo.Staff 
                 
             
         
     

     
还要在Staff.hbm.xml中配置:
 
 
<  query   name  ="Staff.getByLogonAndId"   >
        

这里要特别注意这个要写在的外面,否则会提示Mapping Exception:No Named Query
好了,大公告成了!现在可以跟以前一样使用
appContext.getBean("staffDao"); 这样进行测试了
staffDao.read(new Integer(1));
staffDao.getByLogonAndId("abc",new Integer(2));