Architecture 为什么要将DAO层置于持久层之上(如JDO或Hibernate)

Architecture 为什么要将DAO层置于持久层之上(如JDO或Hibernate),architecture,persistence,data-access-layer,jdo,dao,Architecture,Persistence,Data Access Layer,Jdo,Dao,数据访问对象(DAO)是一种常见的设计模式,由Sun推荐。但最早的Java DAO示例直接与关系数据库交互——本质上,它们是在进行对象关系映射(ORM)。现在,我看到DAO位于成熟的ORM框架之上,比如JDO和Hibernate,我想知道这是否真的是个好主意 我正在开发一个使用JDO作为持久层的web服务,并且正在考虑是否引入DAO。在处理包含其他对象映射的特定类时,我预见到一个问题: public class Book { // Book description in various

数据访问对象(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;
}
公共课堂教材{
//各种语言的书籍描述,按ISO语言代码编制索引
私有地图描述;
}
JDO足够聪明,可以将其映射到“BOOKS”和“bookscriptions”表之间的外键约束。它透明地加载BookDescription对象(我相信使用延迟加载),并在持久化Book对象时持久化它们

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


所以我的问题是,直接在业务层调用JDO(或Hibernate,或任何您喜欢的ORM)有什么错?它的语法已经相当简洁,并且与数据存储无关。将其封装在数据访问对象中有什么好处(如果有的话)?

这取决于层的目标是什么。您在中添加了一个抽象,以提供一组不同于另一组的语义。一般来说,有更多的层次来简化一些事情,例如未来维护的开发。但它们还有其他用途


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

您提出了一些观点。但我仍然使用Dao层,原因如下:

  • 数据库访问是对远程系统的调用。在所有这些情况下(也包括web服务、ajax等),交互的粒度需要足够大。许多微小的呼叫会破坏性能。这种性能需求通常需要系统或层(这里是Dao层)的不同视图

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


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

    根据我以前的经验,我建议使用一个简单的静态facade,比如说
    持久性
    ,为持久性相关的操作提供一个易于使用的高级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);
        ...
    
    
    列出最便宜的书籍=
    查找(“从b.price<?”的图书中选择b”,lowPriceForBooks);
    ...
    书b=新书(…);
    (b);
    ...
    Book existingBook=加载(Book.class,bookId);
    删除(现有的书籍);
    ...
    

    上面的代码尽可能简单,并且可以很容易地进行单元测试。

    实际上,它必须比所有这些答案都要简单。这些模式都是关于层的。您不希望循环引用,因为您制作的层只能知道它们上面的内容。您希望您的UICode能够引用任何和所有服务,您的服务代码能够引用任何和所有DAO

  • 服务
  • UICode
  • POJO从上到下传递。

    一个词:事务

    假设我必须在一个事务中执行两个数据更新操作。这些操作一起构成一个逻辑工作单元。我的业务逻辑想用这个工作单元来表达自己,它不想用事务边界来麻烦自己

    所以我写了一把刀。使用Spring事务和hibernate获取以下伪代码:

    编辑删除了HQL,它非常冒犯@Roger,但与要点无关

    我的业务逻辑调用doUnitOfWork(),它开始一个事务,执行两个持久性操作,然后提交。它既不知道也不关心事务,也不关心执行了哪些操作

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


    通常,我总是将我的数据访问操作包装在DAO中,并敲打它周围的接口。

    我相信大多数DAO是由人们出于历史(历史;])原因添加的。您是对的,它们最初的意思是作为在ORM之前执行CRUD操作所需的SQL胶水的方便封装。如今,有了透明的持久性,它们的角色现在基本上是多余的

    什么是不
    @Transactional
    public void doUnitOfWork() {
      // some persistence operation here
      // some other persistence operation here
    }
    
    //start persistence context
    ...
    Department dept1 = new Department("department1");
    dept1.addEmployee(new Employee("José", 10503f));
    
    em.persist(dept1);
    ...
    //close persistence context
    
    //start persistence context
    ...
    Department aDepart = hibernateSession.load(Department.class, dId);
    Employee anEmployee = hibernateSession.load(Employee.class, eId);
    
    aDepart.addEmployee(anEmployee);     
    ...
    //close persistence context