使用java反射优化Spring自动生成的DAO实现类!

来源:互联网 发布:淘宝和天猫是一家吗 编辑:程序博客网 时间:2024/05/18 02:56

 

   在使用Struts+Hibernate+Spring框架时,发现Spring为应用程序生成的数据访问层的代码都非常的重复,这样给人的感觉非常的不爽,基本上有多少个DAO,每个DAO都是重复的方法,无非就是换了一下具体的相对应的pojo类而已,这样感觉非常违背java代码重用的原则,因此想对此做一下改进,以便让代码的重用率更高一些,真实体现java在方法重用上的优雅性, 我想到了用java反射机制来实现,下面就是具体的代码片段了(关于java反射机制不懂的朋友,可以参考我原来写的关于java反射机制的文章)。

看下面一端代码,它是用来保存Diary这个对象的方法,如果你有一个User,或者Department,或者更多需要保存的对象的话,那么在你的相应的UserDAO,DepartmentDAO类里面下面的方法仍然会再出现一遍,不知道你看着爽不爽,至少我的感觉是非常冗余。

public void save(Diary transientInstance) {
        log.debug(
"saving Diary instance");
        
try {
            getHibernateTemplate().save(transientInstance);
            log.debug(
"save successful");
        }
 catch (RuntimeException re) {
            log.error(
"save failed", re);
            
throw re;
        }

    }

接着我来进行改进,我让所有的具体的DAO类都去实现一个BaseDAO类,在BaseDAO类里面上述方法将如下所示:

/**
     * 保存对象
     * 
     * 
@param object
     
*/

    
public void save(Object object) {
        log.info(
"保存 " + object.getClass().getName() + " 实例");
        
try {
            getHibernateTemplate().save(object);
            log.info(
"保存 " + object.getClass().getName() + " 实例成功");
        }
 catch (RuntimeException re) {
            log.info(
"保存 " + object.getClass().getName() + " 实例失败", re);
            
throw re;
        }

    }

 

上述方法还没真正的使用java反射机制,只是在打印保存对象时,用了一点点,那么接着看下面的代码吧,你就会感受到java反射机制所带来的好处!

按照Id查询对象:

    /**
     * 按照id查询对象
     * 
     * 
@param object
     * 
@param id
     * 
@return
     
*/
    
public Object findById(Class object, java.lang.Integer id) {

        log.info(
"通过: " + id + " 查询" + object.getCanonicalName() + "实例");
        
try {
            Object instance 
= (Object) getHibernateTemplate().get(
                    object.getCanonicalName(), id);
            
return instance;
        } 
catch (RuntimeException re) {
            log.info(
"通过: " + id + " 查询" + object.getCanonicalName() + "实例失败",
                    re);
            
throw re;
        }
    }

一直记得hibernate按照id来查询对象的方式,如 get("Teacher",new Integer(1)),这样如此硬编码的方式在java反射机制面前居然也变的如此灵活,真的是爱上java反射机制没的说。

接着往下看吧

 按照example查询对象,这没什么好说的,就算没有java的反射照样可以完成,只是有了反射之后,更便于我们知道哪里出了问题

 

    /**
     * 按照example查询
     * 
     * 
@param instance
     * 
@return
     
*/
    
public List findByExample(Object instance) {
        log.info(
"使用Example查询 " + instance.getClass().getName() + " 实例");
        
try {
            List results 
= getHibernateTemplate().findByExample(instance);
            log.info(
"使用Example查询 " + instance.getClass().getName()
                    
+ " 实例成功, 查询出的记录数为: " + results.size());
            
return results;
        } 
catch (RuntimeException re) {
            log.info(
"使用Example查询 " + instance.getClass().getName() + " 实例失败",
                    re);
            
throw re;
        }
    }

 

接下来就是一个使用反射的最好的案例了

按照属性查询:按照这种方式做查询的再普通不过了,要使用jdbc的话不知道我们要写多少条String版的sql语句,现在有了spring帮着管理,真的轻松很多,看看吧

认真揣摩这个方法,发现这个方法的代码写的还是相当流畅和优雅,至少我写的话,绝对会非常累赘,里面使用了java反射机制,从而使这个方法变的更通用,在做项目的时候,我在根据不同条件求count的时候,没有认真看这个方法,因为当时遇到的问题是如果使用Hql来做查询时,如果条件有中文出现的话,中文条件将被当作乱码,后来被逼的没办法,居然写成下面的代码:

Query query=null;
      String hql
="select count(*) from Student as s";
      query
=session.createQuery(hql);
     
if(stu.getStatuId()!=null){
       
      hql
=hql+" where s.studentStatu.statuId="+stu.getStatuId();
      query
=session.createQuery(hql);
     }

     
     
if(stu.getClassId()!=null){
      
      hql
=hql+" and s.classes.classesId="+stu.getClassId();
       query
=session.createQuery(hql);
     }

     
if(stu.getTeacherByDegreeTeacherId()!=null){
      
      hql
=hql+" and s.teacherByDegreeTeacherId.teacherId="+stu.getTeacherByDegreeTeacherId().getTeacherId();
       query
=session.createQuery(hql);
     }

     
     
if(stu.getStuNo()!=null){
      
      hql
=hql+" and s.stuNo='"+stu.getStuNo()+"'";
       query
=session.createQuery(hql);
     }

     
     
if(stu.getEmployeWay()!=null){
      hql
=hql+" and s.employeWay='"+stu.getEmployeWay()+"'";
      query
=session.createQuery(hql);
     }

     
     
if(stu.getCreateDate()!=null){
      String date
=stu.getCreateDate().toString();
      hql
=hql+" and s.createDate='"+date+"'";
      query
=session.createQuery(hql);
     }

     
if(stu.getIsObtain()!=null){
      hql
=hql+" and s.isObtain=?";
       query
=session.createQuery(hql);
       query.setString(
0, stu.getIsObtain());
     }

     
     
if(stu.getWorkIntentCity()!=null ){
       hql
=hql+" and s.workIntentCity=?";
       query
=session.createQuery(hql);
       query.setString(
0, stu.getWorkIntentCity());
     }

     
     
if(stu.getAddress()!=null ){
       hql
=hql+" and s.address like ?";
       query
=session.createQuery(hql);
       query.setString(
0"%"+stu.getWorkIntentCity()+"%");
     }

     
      
     
if(stu.getStuName()!=null){
      hql
=hql+" and s.stuName=?";
      query
=session.createQuery(hql);
      
if(stu.getWorkIntentCity()!=null){
          
          query.setString(
0, stu.getWorkIntentCity());
          query.setString(
1, stu.getStuName());
      }
else{
       query.setString(
0, stu.getStuName());
      }

          
     }

     System.out.println(
"-----------hql----------------------:"+hql);
     
 
return query.list();


只所以把代码写的如此愚蠢,就是因为一个原因:query.setString()这个方法,因为通过它把中文加入查询条件的话,就不会出现乱码了,而为了达到这一目的,代码就被我写成这种模样,想想当时我还自以为是的样子真是可笑,我都没把hibernate里面的方法整明白就乱写一通,为什么就不知道find(String,Object [] values)这个方法已经存在了呢?我只想到有find(String,object value),晕!

不过幸好后来意识到这样写的坏处,根据条件求count的方法又变成下面的这种方式

 

/**
  * 根据DetachedCriteria来获取符合条件的人数
  * 
@param s
  * 
@return int对象所在的集合
  
*/

 
public int  getTotalCountByDetachedCriteria(Student s)
 
{
      log(
"根据DetachedCriteria来获取符合条件的人数");
  DetachedCriteria dc 
= DetachedCriteria.forClass(Student.class);
  
  
/*ProjectionList proList = Projections.projectionList();
  proList.add(Projections.count("stuId"));
  dc.setProjection(proList);
  
*/

  
//设置要查询的属性,即count
  dc.setProjection(Projections.count("stuId"));
  
  
//设置查询条件
  
//如果直接根据Example来创建的话,无法把stu里面的非空属性赋为查询条件,所以只有根据是否非空一步步加入
  
//这一点没有criteria做的好,因为是criteria的话,根据dc.add(Example.create(s))会自动把非空属性加入where条件里面,
  
//不知道为什么DetachedCriteria里面没有这种方法呢?浪费那么多时间编些没有意义的代码
  
//dc.add(Example.create(s));
 
  
  
if(s.getStuName()!=null)
   dc.add(Expression.eq(
"stuName", s.getStuName()));
  
if(s.getStatuId()!=null)
   dc.add(Expression.eq(
"statuId", s.getStatuId()));
  
if(s.getClasses()!=null)
   dc.add(Expression.eq(
"classes", s.getClasses()));
  
if(s.getWorkIntentCity()!=null)
   dc.add(Expression.eq(
"workIntentCity", s.getWorkIntentCity()));
  
if(s.getStuNo()!=null)
   dc.add(Expression.eq(
"stuNo", s.getStuNo()));
  
if(s.getEmployeWay()!=null)
   dc.add(Expression.eq(
"employeWay",s.getEmployeWay()));
  
if(s.getIsObtain()!=null)
   dc.add(Expression.eq(
"isObtain", s.getIsObtain()));
  
if(s.getTeacherByDegreeTeacherId()!=null)
   dc.add(Expression.eq(
"teacherByDegreeTeacherId", s.getTeacherByDegreeTeacherId()));
   
  List list 
= stuDAO.findCountByDetachedCriteria(dc);
  log(
"---------------根据DetachedCriteria来获取totalcount-----"+ Integer.valueOf(list.get(0).toString()));
  
return list.size() == 0 ? null : Integer.valueOf(list.get(0).toString());
 }

这样写的方式比上面写的方式稍微改进了一点点,至少把业务逻辑写在了业务层,而不像上面的那种方式在持久层做判断,但缺点似乎还是没有被改进—没法做为公共方法,这样造成的结果求不同类别的count这种方法都得重新写一遍,造成代码太过冗余,试着优化一下吧,优化后的方式如下:
 

public Integer findCountByCriteria(final Object o){
  
try
  {
   
return (Integer) getHibernateTemplate().execute(new HibernateCallback()
   {
    
public Object doInHibernate(Session session) throws HibernateException
    {
     log.info(
"o.getClass()"+o.getClass().toString());
     Field [] f
=o.getClass().getFields();
     
for(int i=0;i<f.length;i++)
          System.out.println(f[i].getName()
+"-oo");
     Criteria criteria
=session.createCriteria(o.getClass());
     criteria.add(Example.create(o));
     log.info(criteria.toString());
    
    criteria.setProjection(Projections.count(
"stuId"));
     
     
return criteria.list().get(0);
    }
   }, 
true);
  }
  
catch (RuntimeException re)
  {
   
   
throw re;
  }
 }

仔细的分析这个代码段,唯一的缺陷就是设置查询的字段这个地方是硬编码的形式,其他的代码几乎都是用反射来实现的,因此这个方法也可以作为一个公有的方法放在BaseDAO中,这样在求类似的count值时就可以避免把相似的代码写N遍,还有一个好处是因为使用了Criteria这个类,因此就不再需要判断哪些条件该加,哪些条件不该加,这样可以减少大部分的无用乃至枯燥的if语句,但这个方法的不足之处就是在设置查询的字段时因为不能写成Projections.count("*")的形式,所以必须得赋你要查询的类的一个字段,如果要使用反射机制的话,这个字段必须是公共的,因为使用getField()方法取得的类变量是public类型的,我们可以让所有的pojo都继承一个类,在这个类中声明一个public类型的变量,这样问题就可以得到解决

好了,这些都是在做项目中遇到的和解决的,仅此拿出来互相分享和讨论,因为一贯欣赏java的开源原则,呵呵,虽然我的代码很乱,甚至丑陋,但毕竟还在学习,所以相信会写出优雅的代码的一天

原创粉丝点击