为什么在持久层(如JDO或Hibernate)上放置DAO层

Tod*_*wen 32 architecture persistence dao data-access-layer jdo

数据访问对象(DAO)是一种常见的设计模式,由Sun推荐.但最早的Java DAO示例直接与关系数据库进行交互 - 它们本质上是做对象关系映射(ORM).如今,我看到DAO在成熟的ORM框架之上,如JDO和Hibernate,我想知道这是不是一个好主意.

我正在使用JDO作为持久层开发Web服务,并且正在考虑是否引入DAO.我在处理包含其他对象映射的特定类时遇到了问题:

public class Book {
    // Book description in various languages, indexed by ISO language codes
    private Map<String,BookDescription> descriptions;
}
Run Code Online (Sandbox Code Playgroud)

JDO足够聪明,可以将其映射到"BOOKS"和"BOOKDESCRIPTIONS"表之间的外键约束.它透明地加载BookDescription对象(我​​相信使用延迟加载),并在Book对象持久化时保留它们.

如果我要引入一个"数据访问层"并编写一个像BookDao这样的类,并将所有JDO代码封装在其中,那么这个JDO对子对象的透明加载不会绕过数据访问层吗?为了保持一致性,不应该通过一些BookDescriptionDao对象(或BookDao.loadDescription方法)加载和保存所有BookDescription对象吗?然而,以这种方式进行重构将使得模型的操作变得不必要地复杂化.

所以我的问题是,在业务层中直接调用JDO(或Hibernate,或者你想要的任何ORM)有什么问题?它的语法已经非常简洁,并且与数据存储区无关.如果有的话,将它封装在数据访问对象中有什么好处?

KLE*_*KLE 12

你提出了一些观点.但我仍然使用Dao层,这就是为什么:

  1. 数据库访问是对远程系统的调用.在所有这些情况下(也就是web服务,ajax等......),交互的粒度必须足够大.许多微小的电话会扼杀性能.这种性能必然要求系统或层(这里是Dao层)的不同视图.

  2. 有时,持久性操作仅用于加载/保存/删除对象.一个独特的Dao(或超类;考虑泛型)可以对此负责,因此您不必一次又一次地编写这些方法.
    但通常,您也有特定需求,例如运行不是由ORM自动创建的特定请求.在那里,您使用特定的Dao方法编写您的特定需求(通常可以重用).
    在同一层中具有常规和特定需求允许重用(例如,拦截可以确保在需要时打开/提交数据库连接).


8bi*_*kie 11

随着时间的推移,DAO失去了意义.

在它成为流行模式的J2EE时代,DAO是一个类,您可以同时满足多个数据源 - 一个供应商的数据库,另一个供应商的数据库,一个文件 - 并提供一个地方来包装查询沟通数据.

有足够的重用空间,因此特定实体的DAO对象可以很好地扩展一个抽象的DAO,它包含可重用的东西,它本身实现了一个DAO接口.

在J2EE/EJB之后,DataMapper和DataSource模式(或简单系统,ActiveRecord)变得流行起来以执行相同的角色.然而,DAO成为任何涉及持久性的对象的流行语.

现在,"DAO"一词遗憾地成为"一个让我与我的数据库进行通信的类"的同义词.

使用ORM/JPA,真正的J2EE时代DAO的大部分理由都是开箱即用的.

对于后一种DataSource模式,JPA的EntityManager类似于DataSource,但通常通过PersistenceUnit XML定义提供,并通过IoC实例化.

曾经存在于DAO或Mapper中的CRUD方法现在可以使用Repository模式提供一次.不需要AbstractDAO - ORM产品足够聪明,可以接受Object()并知道它在哪里持久化.

  • +1感谢您对此系列替代模式(DAO,DataSource,ActiveRecord,Repository)进行编目.让我想知道下一件大事将是什么...... :) (2认同)

Pre*_*gha 10

这取决于你的图层的目标是什么.你把一个抽象放在另一个集合上提供一组不同的语义.通常还有其他层来简化某些事情,例如未来维护的发展.但他们可以有其他用途.

例如,ORM代码上的DAO(或持久性处理)层提供了您不希望污染业务逻辑的专用恢复和错误处理功能.


Rog*_*rio 6

使用JDO或JPA等ORM工具时,DAO是一种反模式.在这种情况下,创建"数据访问层"是完全没有必要的,只会增加代码库的额外代码和复杂性,使其更难开发和维护.

根据我之前的经验,我建议使用简单的静态外观,例如Persistence,为持久性相关操作提供易于使用的高级API.

然后,您可以使用静态导入在任何有用的地方轻松访问这些方法.例如,您可以使用以下代码:


    List<Book> cheapBooks = 
        find("select b from Book where b.price < ?", lowPriceForBooks);
    ...
    Book b = new Book(...);
    persist(b);
    ...
    Book existingBook = load(Book.class, bookId);
    remove(existingBook);
    ...
Run Code Online (Sandbox Code Playgroud)

上面的代码尽可能简单易用,并且可以轻松进行单元测试.

  • 我很高兴看到你把DAO称为反模式!但是......不是你的静态持久性外观在概念上与DAO完全相同吗?我没有看到将单行JDO方法抽象为单行静态方法的好处,加上抽象是"漏洞",因为它需要使用底层ORM的查询语言. (4认同)
  • DAO反模式?那你如何对你的客户端(服务?)逻辑进行单元测试呢?真的想用JPA查询字符串污染这个吗?应用分页,排序怎么样?我真的不想打扰这项任务的服务. (3认同)
  • 另外:看到这个解释为什么我非常不同意:http://www.olivergierke.de/wordpress/2009/01/se-radio-episode-121-or-mappers/ (3认同)
  • 当使用ORM时,是的,DAO是一种反模式.正如我在我的回答中所说,单元测试不是问题,因为有很好的模拟工具可以用于此.JPA查询(在字符串中或不在字符串中)不是"数据访问代码",如果这是您的想法; 它们是业务逻辑代码.而且,根据我的经验,在代码中使用字符串是编写查询的最有效方法.分页和排序通常是UI问题,需要在UI代码中处理(尽管使用"order by"是一个很好的优化). (2认同)

ska*_*man 6

一句话:交易

假设我必须在单个事务中执行两个数据更新操作.这些操作共同构成了一个逻辑工作单元.我的业务逻辑想要用这个工作单元来表达自己,并且它不想打扰自己的事务边界.

所以我写了一个DAO.使用Spring事务和hibernate获取此伪代码:

编辑以删除那些冒犯@Roger的HQL,但这与该点无关

@Transactional
public void doUnitOfWork() {
  // some persistence operation here
  // some other persistence operation here
}
Run Code Online (Sandbox Code Playgroud)

我的业务逻辑调用doUnitOfWork(),它开始一个事务,执行持久性操作,然后提交.它既不知道也不关心交易,或者执行什么操作.

此外,如果DAO使用doUnitOfWork()方法实现接口,那么业务逻辑可以编码到接口,从而更容易进行单元测试.

通常,我总是将我的数据访问操作包装在DAO中,然后敲击它周围的界面.