论坛首页 Java企业应用论坛

Spring的DAO设计实践

浏览 108719 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (1) :: 隐藏帖 (0)
作者 正文
   发表时间:2004-12-22  
jeffrey_he 写道
我是用一个类将HibernateDaoSupport封装做为整个DAO层,SERVICE层做一个抽象类,用IOC注入DAO层的那个类,所有SERVICE层的实现类从该抽象类继承。调用DAO层的类实现数据库存储。


能否show一下你的DAO层如何封装的呢?
0 请登录后投票
   发表时间:2004-12-22  
flyromza 写道
我现在做的简单实现是这样的:

dao那边分为几类,有一个BaseDAO,执行三个方法save,update,saveorupdate。

还有一个find类,有直接利用HibernateTemplate方法的简单find,也有自定义的find。

另外还可能有一个remove类,可能要支持一些复杂的remove。

service这边暂时就一个service,通过Aop的方式,注入了BaseDAO和FindDAO。

我希望dao层保持通用性,而一些变化和组合在service上面来做,所以就把dao分的比较细。

代码简单如下:

BaseDAO:
public interface IBaseDAO {

  public void saveObject(Object o);;

  public void updateObject(Object o);;

  public void storeObject(Object o);;

}


FindDAO:
public interface IFindDAO {

  public Object findbyid(Class clazz, String id);;

  public List findforpage(String querystring, Class clazz, int firstnode, int noderange);;

  public int findallcount(Class clazz);;

}


service:
public interface IService extends IBaseDAO, IFindDAO {

  public void setdao1(IBaseDAO basedao);;

  public void setdao2(IFindDAO finddao);;

}


这样的实现有什么缺点么? 请不吝赐教。



Service继承DAO?不大好吧,成了tight-coupling了
依靠IoC/AOP/Pattern进行编程,精华就在于分离,分离,再分离。
Service接口最多只能通过Composition的方式和DAO打交道
0 请登录后投票
   发表时间:2004-12-23  
dao的例子通常都只见到单表的。
而我们的查询常常都会涉及多表的

多表dao的查询,更新。。。。。
有例子吗?
0 请登录后投票
   发表时间:2004-12-23  
copenhagen 写道
flyromza 写道
我现在做的简单实现是这样的:

dao那边分为几类,有一个BaseDAO,执行三个方法save,update,saveorupdate。

还有一个find类,有直接利用HibernateTemplate方法的简单find,也有自定义的find。

另外还可能有一个remove类,可能要支持一些复杂的remove。

service这边暂时就一个service,通过Aop的方式,注入了BaseDAO和FindDAO。

我希望dao层保持通用性,而一些变化和组合在service上面来做,所以就把dao分的比较细。

代码简单如下:

BaseDAO:
public interface IBaseDAO {

  public void saveObject(Object o);;

  public void updateObject(Object o);;

  public void storeObject(Object o);;

}


FindDAO:
public interface IFindDAO {

  public Object findbyid(Class clazz, String id);;

  public List findforpage(String querystring, Class clazz, int firstnode, int noderange);;

  public int findallcount(Class clazz);;

}


service:
public interface IService extends IBaseDAO, IFindDAO {

  public void setdao1(IBaseDAO basedao);;

  public void setdao2(IFindDAO finddao);;

}


这样的实现有什么缺点么? 请不吝赐教。



Service继承DAO?不大好吧,成了tight-coupling了
依靠IoC/AOP/Pattern进行编程,精华就在于分离,分离,再分离。
Service接口最多只能通过Composition的方式和DAO打交道


我感觉Service里面的一些方法也是调用DAO的方法,如果修改了DAO的话,Service也是需要被修改的,like this:

public Object getObject(Class clazz, Serializable id); {
        return dao.getObject(clazz, id);;
    }


即便我不把getObject作为Service的接口,当DAO修改了,我一样要去修改Service。

除了注入DAO对象之外,Service怎么才能避免和DAO联系不是那么紧密呢?
0 请登录后投票
   发表时间:2004-12-23  
flyingbug 写道
jeffrey_he 写道
我是用一个类将HibernateDaoSupport封装做为整个DAO层,SERVICE层做一个抽象类,用IOC注入DAO层的那个类,所有SERVICE层的实现类从该抽象类继承。调用DAO层的类实现数据库存储。


能否show一下你的DAO层如何封装的呢?


就是把HibernateTemplate的一些方法封装一下,我不想写那么多DAO,因为和service层很多方法重复了,所以就这么做了。只封装了部分,还有一些用到时再封装吧。

    /**
     * 向数据库添加一条对应于一个业务对象实例的记录
     * 
     * @param entity
     *            业务对象实例
     * @throws DaoException
     *             当添加记录失败时抛出异常
     */
    public Entity create(Entity entity); throws DaoException {
        try {
            getHibernateTemplate();.save(entity);;
            return entity;
        } catch (DataAccessException e); {
            log.error("保存 " + entity.getClass();.getName(); + " 实例到数据库失败", e);;
            throw new DaoException("保存 " + entity.getClass();.getName();
                    + " 实例到数据库失败", e);;
        }
    }

    /**
     * 向数据库更新一条对应于一个业务对象实例的记录
     * 
     * @param entity
     *            业务对象实例
     * @throws DaoException
     *             当更新记录失败时抛出异常
     */
    public void update(Entity entity); throws DaoException {
        try {
            getHibernateTemplate();.update(entity);;
        } catch (DataAccessException e); {
            log.error("更新 " + entity.getClass();.getName(); + " 实例到数据库失败", e);;
            throw new DaoException("更新 " + entity.getClass();.getName();
                    + " 实例到数据库失败", e);;
        }
    }

    /**
     * 从数据库删除一条对应于一个业务对象的记录
     * 
     * @param entity
     *            业务对象实例
     * @throws DaoException
     *             当删除记录失败时抛出异常
     */
    public void delete(Entity entity); throws DaoException {
        try {
            getHibernateTemplate();.delete(entity);;
        } catch (DataAccessException e); {
            log.error("从数据库删除 " + entity.getClass();.getName(); + " 实例失败", e);;
            throw new DaoException("从数据库删除 " + entity.getClass();.getName();
                    + " 实例失败", e);;
        }
    }

    /**
     * 从数据库删除所有对应于一个业务对象的记录
     * 
     * @param clazz
     *            指定类型的业务对象
     * @throws DaoException
     *             当删除记录失败时抛出异常
     */
    public void deleteAll(Class clazz); throws DaoException {
        try {
            List result = getHibernateTemplate();.loadAll(clazz);;
            getHibernateTemplate();.deleteAll(result);;
        } catch (DataAccessException e); {
            log.error("从数据库删除 " + clazz.getName(); + " 的所有记录失败", e);;
            throw new DaoException("从数据库删除 " + clazz.getName(); + " 的所有记录失败", e);;
        }
    }
    
    public void deleteAll(Collection entities); throws DaoException {
        try {
            getHibernateTemplate();.deleteAll(entities);;
        } catch(DataAccessException e); {
            throw new DaoException(e);;
        }
    }

    /**
     * 根据关键字从数据库加载指定类型的业务对象。
     * 
     * @param clazz
     *            业务对象的Class
     * @param keyName
     *            指定关键字对应的字段名称
     * @param keyValue
     *            指定关键字的值
     * @return <ul>
     *         <li>当关键字唯一并存在该记录时,返回该记录对应的业务对象</li>
     *         <li>当关键字不唯一,返回查询结果的第一条记录所对应的业务对象</li>
     *         <li>当不存在该记录时,返回null</li>
     *         </ul>
     * @throws DaoException
     *             当加载记录失败时抛出异常
     */
    public Object loadByKey(Class clazz, String keyName, Object keyValue);
            throws DaoException {
        try {
            List result = getHibernateTemplate();.find(
                    "from " + clazz.getName(); + " where " + keyName + " = ?",
                    keyValue);;
            if (result != null && result.size(); > 0); {
                return result.get(0);;
            } else {
                return null;
            }
        } catch (DataAccessException e); {
            log.error("加载 " + keyName + " 为 " + keyValue + " 的 "
                    + clazz.getName(); + " 实例失败", e);;
            throw new DaoException("加载 " + keyName + " 为 " + keyValue + " 的 "
                    + clazz.getName(); + " 实例失败", e);;
        }
    }

    /**
     * 从数据库加载指定类型的业务对象的所有记录。
     * 
     * @param clazz
     *            业务对象的Class
     * @return 返回数据库中对应该业务对象的所有记录的集合
     * @throws DaoException
     *             当加载记录失败时抛出异常
     */
    public List loadAll(Class clazz); throws DaoException {
        try {
            return getHibernateTemplate();.loadAll(clazz);;
        } catch (DataAccessException e); {
            log.error("加载所有 " + clazz.getName(); + " 实例时失败", e);;
            throw new DaoException("加载所有 " + clazz.getName(); + " 实例时失败", e);;
        }
    }

    /**
     * 根据查询语句查询数据库并返回查询结果所包含的业务对象集合。
     * 
     * @param queryString
     *            指定查询语句
     * @return 返回查询结果包含的业务对象集合
     * @throws DaoException
     *             当查询失败时抛出异常
     */
    public List find(String queryString); throws DaoException {
        try {
            return getHibernateTemplate();.find(queryString);;
        } catch (DataAccessException e); {
            log.error("执行查询 " + queryString + " 失败", e);;
            throw new DaoException("执行查询 " + queryString + " 失败", e);;
        }
    }

    /**
     * 根据带一个参数的查询语句查询数据库并返回查询结果所包含的业务对象集合。
     * 
     * @param queryString
     *            指定查询语句
     * @param param
     *            指定所带参数
     * @return 返回查询结果包含的业务对象集合
     * @throws DaoException
     *             当查询失败时抛出异常
     */
    public List find(String queryString, Object param); throws DaoException {
        try {
            return getHibernateTemplate();.find(queryString, param);;
        } catch (DataAccessException e); {
            log.error("执行参数为 " + param + " 的查询 " + queryString + " 失败", e);;
            throw new DaoException("执行参数为 " + param + " 的查询 " + queryString
                    + " 失败", e);;
        }
    }

    /**
     * 根据带多个参数的查询语句查询数据库并返回查询结果所包含的业务对象集合。
     * 
     * @param queryString
     *            指定查询语句
     * @param params
     *            指定参数数组
     * @return 返回查询结果包含的业务对象集合
     * @throws DaoException
     *             当查询失败时抛出异常
     */
    public List find(String queryString, Object[] params); throws DaoException {
        try {
            return getHibernateTemplate();.find(queryString, params);;
        } catch (DataAccessException e); {
            StringBuffer paramString = new StringBuffer("");;
            for (int i = 0; i < params.length; i++); {
                paramString.append(params[i]);;
                paramString.append(" ");;
            }
            log.error("执行参数为 " + paramString + "的查询 " + queryString + " 失败", e);;
            throw new DaoException("执行参数为 " + paramString + "的查询 "
                    + queryString + " 失败", e);;
        }
    }

    /**
     * 根据已定义的查询语句查询数据库并返回查询结果所包含的业务对象集合。
     * 
     * @param queryName
     *            已定义查询语句的名称
     * @return 返回查询结果包含的业务对象集合
     * @throws DaoException
     *             当查询失败时抛出异常
     */
    public List findByNamedQuery(String queryName); throws DaoException {
        try {
            return getHibernateTemplate();.findByNamedQuery(queryName);;
        } catch (DataAccessException e); {
            log.error("执行命名为 " + queryName + " 的查询失败");;
            throw new DaoException("执行命名为 " + queryName + " 的查询失败");;
        }
    }

    /**
     * 根据已定义的带一个参数的查询语句查询数据库并返回查询结果所包含的业务对象集合。
     * 
     * @param queryName
     *            已定义查询语句的名称
     * @param param
     *            指定的参数
     * @return 返回查询结果包含的业务对象集合
     * @throws DaoException
     *             当查询失败时抛出异常
     */
    public List findByNamedQuery(String queryName, Object param);
            throws DaoException {
        try {
            return getHibernateTemplate();.findByNamedQuery(queryName, param);;
        } catch (DataAccessException e); {
            log.error("执行参数为 " + param + " 命名为 " + queryName + " 的查询失败");;
            throw new DaoException("执行参数为 " + param + " 命名为 " + queryName
                    + " 的查询失败");;
        }
    }

    /**
     * 根据已定义的带多个参数的查询语句查询数据库并返回查询结果所包含的业务对象集合。
     * 
     * @param queryName
     *            已定义查询语句的名称
     * @param params
     *            指定的参数数组
     * @return 返回查询结果包含的业务对象集合
     * @throws DaoException
     *             当查询失败时抛出异常
     */
    public List findByNameQuery(String queryName, Object[] params);
            throws DaoException {
        try {
            return getHibernateTemplate();.findByNamedQuery(queryName, params);;
        } catch (DataAccessException e); {
            StringBuffer paramString = new StringBuffer("");;
            for (int i = 0; i < params.length; i++); {
                paramString.append(params[i]);;
                paramString.append(" ");;
            }
            log.error("执行参数为 " + paramString + "命名为 " + queryName + " 的查询失败");;
            throw new DaoException("执行参数为 " + paramString + "命名为 " + queryName
                    + " 的查询失败");;
        }
    }
0 请登录后投票
   发表时间:2004-12-23  
flyromza 写道

即便我不把getObject作为Service的接口,当DAO修改了,我一样要去修改Service。

除了注入DAO对象之外,Service怎么才能避免和DAO联系不是那么紧密呢?


Service和DAO是在两个层面上,Service实现的是逻辑,而DAO实现的是数据操作,照你的接口的写法,Service又实现逻辑,又实现数据操作,那你的DAO Pattern就没有什么意义了。更何况,在注入DAO实现的情况下,修改DAO接口,你必须重构的也仅仅是DAO实现而已;如果照你现在这样的做法,任何对DAO接口的修改,势必会波及到所有的Service实现和DAO实现。

关于loose-coupling,我觉得,只要在Service中引用到了DAO,就必然存在Coupling,我们能做到的只能是尽可能的loose it而不是decouple it。需要修改 != Bad Design。Depends on what kind of modification on your codes.
0 请登录后投票
   发表时间:2004-12-23  
to:jeffrey_he

恩,使用NamedQuery,很流行哦
如果是这样,那我还是建议你把它做成command模式的吧
把find、save、update、delete都单独做成一个对象
这种做法也不错,和你的很像但比你的简练,但是和你的缺点一样,就是service层要负责传入HQL,所以service需要依赖DAO层的实现

见:http://www.ociweb.com/jnb/jnbNov2003.html
0 请登录后投票
   发表时间:2004-12-23  
谢谢你的意见。

我现在的想法是,把DAO封装的相对固定,将能想到的操作都包含进去,避免以后大幅度的修改。

Service就是将DAO自由的拼装组合,组合的原则就是自己的业务逻辑。

其实前面我做的Service设计,完全可以不去extends DAO的接口,只是我照着appfuse写Service,发现它的Service就是去调用DAO的方法,所以我认为Service和DAO联系是很紧密的。

顺便问一句,spring是不是提供对分页的封装? 我现在是自己做了一个findforpage来支持分页查询,如果已经有了封装就不再重复发明了。。不过查了查源码,似乎没有找到以page起始的类名。。
0 请登录后投票
   发表时间:2004-12-23  
flyromza 写道
谢谢你的意见。

我现在的想法是,把DAO封装的相对固定,将能想到的操作都包含进去,避免以后大幅度的修改。

Service就是将DAO自由的拼装组合,组合的原则就是自己的业务逻辑。

其实前面我做的Service设计,完全可以不去extends DAO的接口,只是我照着appfuse写Service,发现它的Service就是去调用DAO的方法,所以我认为Service和DAO联系是很紧密的。

顺便问一句,spring是不是提供对分页的封装? 我现在是自己做了一个findforpage来支持分页查询,如果已经有了封装就不再重复发明了。。不过查了查源码,似乎没有找到以page起始的类名。。


Spring没怎么用过,不确认,但似乎是有的。如果你用Eclipse,可以使用Open  Class功能,输入*Page*试试看
0 请登录后投票
   发表时间:2005-02-25  
说一句不大着调的话, 分出程序结构清晰的层次却是是一件好事, 但是过分强调框架,分层.而忽略java本身的语言特性,和简单足够解决问题的原则, 我觉得分太多层加大学习成本, 降低程序效率, 一个简单的, 有效率, 方便维护的代码, 解决方案, 就是满足需要的。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics