posts - 32, comments - 153, trackbacks - 0, articles - 0
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

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

Posted on 2006-11-26 21:08 Zou Ang 阅读(5263) 评论(8)  编辑  收藏 所属分类:

由于要求在项目中使用泛型的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<T , PK extends Serilizable> 是泛型的 Dao 接口:

/**
 * 2006-11-22
 * 范型DAO接口
 * 
@author  Zou Ang
 * Contact <a href ="mailto:richardeee@gmail.com">Zou Ang</a>
 
*/

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 <a href ="mailto:richardeee@gmail.com">Zou Ang</a>
 
*/

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<? extends Object> 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);
    }

}


然后就到了配置文件了:

       <!--   Start :范型DAO配置   -->  
     
<  bean   id  ="abstractDaoTarget"  
        class 
="com.gdnfha.atcs.common.service.dao.hibernate.GenericDaoHibernateImpl"  
        abstract 
="true"   >  
         
<  property   name  ="sessionFactory"   >  
             
<  ref   local  ="sessionFactory"     />  
         
</  property  >  
         
<  property   name  ="namingStrategy"   >  
             
<  ref   bean  ="simpleFinderNamingStratrgy"     />  
         
</  property  >  
     
</  bean  >  
 
     
<  bean   id  ="abstractDao"  
        class 
="org.springframework.aop.framework.ProxyFactoryBean"  
        abstract 
="true"   >  
         
<  property   name  ="interceptorNames"   >  
             
<  list  >  
                 
<  value  >  finderIntroductionAdvisor  </  value  >  
             
</  list  >  
         
</  property  >  
     
</  bean  >  
 
     
<  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  </  value  >  
         
</  property  >  
     
</  bean  >  
 
     
<  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"   />  
      
<!--   End: 范型DAO配置   -->  
 
     
<!--   Start: 测试范型DAO   -->  
  
     
<  bean   id  ="staffDao"   parent  ="abstractDao"   >  
         
<  property   name  ="proxyInterfaces"   >  
             
<  value  >  com.gdnfha.atcs.maintain.service.dao.IStaffDAO      </  value  >  
         
</  property  >  
         
<  property   name  ="target"   >  
             
<  bean   parent  ="abstractDaoTarget"   >  
                 
<  constructor-arg  >  
                     
<  value  >  com.gdnfha.atcs.common.pojo.Staff  </  value  >  
                 
</  constructor-arg  >  
             
</  bean  >  
         
</  property  >  
     
</  bean  >  
 
     
<!--    End:测试范型DAO   -->  


还要在Staff.hbm.xml中配置:

<  query   name  ="Staff.getByLogonAndId"   >   
        
<![CDATA[  select s from Staff s where s.staffLogon = ? and s.staffId = ?   ]]>   
</  query  >  

这里要特别注意<query></query>这个要写在<class></class>的外面,否则会提示Mapping Exception:No Named Query

好了,大公告成了!现在可以跟以前一样使用

appContext.getBean("staffDao"); 这样进行测试了

staffDao.read(new Integer(1));

staffDao.getByLogonAndId("abc",new Integer(2));


评论

# re: 使用AOP实现类型安全的泛型DAO  回复  更多评论   

2006-11-27 10:18 by Nicholas Ding
原文的翻译在 http://www.nirvanastudio.org/java/dont-repeat-the-dao.html

搂主有没有道德啊,抄别人的代码还不忘把Author改成自己。

# re: 使用AOP实现类型安全的泛型DAO  回复  更多评论   

2006-11-27 11:03 by Zou Ang[匿名]
这篇文章是我写的啊,图也是我画的啊,我用在项目里,叫别人有问题来找我有什么不对

# re: 使用AOP实现类型安全的泛型DAO  回复  更多评论   

2006-11-27 11:20 by
哇~~~你们组也搞这个??不会延期得更厉害么?

# re: 使用AOP实现类型安全的泛型DAO  回复  更多评论   

2006-11-27 11:52 by Zou Ang[匿名]
好,为了尊重他人的劳动成果,我把原作者加上

# re: 使用AOP实现类型安全的泛型DAO  回复  更多评论   

2006-12-07 15:52 by zhongbai
按以上的配置,事务要怎样处理呢?
按常规方法配置事务后,引介拦截器 FinderIntroductionInterceptor 会出错,因为配置事务后 拦截到的已不是 FinderExecutor 接口的实现了,而是其包装器,所以会报告错误,不知道你们是怎么处理的呀??

# re: 使用AOP实现类型安全的泛型DAO  回复  更多评论   

2006-12-08 06:24 by Zou Ang
要在DAO层加上事务么?为什么不直接在Business Logic层加事务呢

# re: 使用AOP实现类型安全的泛型DAO  回复  更多评论   

2006-12-08 19:32 by zhongbai
是的,正如你所说,我把事务配置在了业务逻辑层,但是Spring启动时报告数据源Bean循环引用的问题,不知道您在使用中是否遇到该问题?是如何解决的呢?
http://www.javaeye.com/topic/38166 有我对该问题的详细描述,希望你能不吝指点。

# re: 使用AOP实现类型安全的泛型DAO  回复  更多评论   

2008-04-03 10:27 by 海狼
请问大侠们,使用这个Dao,的那个方法实现分页呀?在映射文件里面怎么设置?

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


网站导航: