33

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

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

public class Book {
    // Book description in various languages, indexed by ISO language codes
    private Map<String,BookDescription> descriptions;
}

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

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

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

4

10 回答 10

13

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

  1. 数据库访问是对远程系统的调用。在所有这些情况下(也包括 web 服务、ajax 等),交互的粒度需要足够大。许多微小的调用会影响性能。这种性能需求通常需要对系统或层(此处为 Dao 层)有不同的看法。

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

于 2009-09-04T10:38:03.803 回答
12

随着时间的推移,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 方法现在可以使用存储库模式仅提供一次。不需要 AbstractDAO - ORM 产品足够聪明,可以接受 Object() 并知道它在哪里持久化。

于 2012-04-09T17:25:10.390 回答
10

这取决于您的图层的目标是什么。您放入一个抽象来提供一组不同的语义而不是另一组。通常还有更多的层来简化某些事情,例如未来维护的开发。但它们可能还有其他用途。

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

于 2009-09-04T10:31:10.573 回答
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);
    ...

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

于 2009-09-04T12:09:35.727 回答
6

一个词:交易

以我必须在单个事务中执行两个数据更新操作的情况为例。这些操作共同构成了一个逻辑工作单元。我的业务逻辑想用那个工作单元来表达自己,它不想用事务边界来打扰自己。

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

编辑以删除非常冒犯@Roger但与重点无关的HQL

@Transactional
public void doUnitOfWork() {
  // some persistence operation here
  // some other persistence operation here
}

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

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

通常,我总是将我的数据访问操作包装在 DAO 中,并围绕它创建一个接口。

于 2009-09-04T12:25:39.053 回答
3

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

现在合适的是存储库和服务的概念:

Repository:存储在 ORM 特定代码(例如,Hibernate 或 JDO)中实现的查询方法集合的类

通常,您可以创建一个抽象基类 Repository,然后提供一个 ORM 特定的实现,您可以在其中以特定于您的 ORM 的代码实现所有查询方法。这种方法的好处是您可以创建一个 MockRepository 实现来帮助测试您的应用程序,而无需使用数据库。

服务:存储方法集合的类,这些方法可以编排对对象模型的重要更改/添加(通常是 ORM 独立代码)。

这有助于使您的应用程序在很大程度上保持 ORM 独立 - 将应用程序移植到另一个 ORM 实际上只涉及新的 ORM 特定存储库类的实现。

于 2010-11-24T01:53:26.237 回答
3

我认为“每个实体的 DAO 类”模式对于 ORM 管理的数据层来说绝对是多余的。相反,DAO 层应该由一组适用于任意实体类的通用 CRUD 方法集和大量对数据执行更复杂操作的方法组成。如果功能足够大,那么 DAO 层应该根据领域标准分成多个类,这使得该方法更类似于面向服务的架构。

于 2011-06-17T07:28:18.050 回答
3

所有这些对层的介绍的目的是使可维护性变得容易和简单。

  1. 数据访问层
  2. 业务层
  3. 表示层

第一层(数据访问层)的目的是处理数据库逻辑并防止业务层了解任何数据库细节。
数据访问层使用 POJO 或 EJB (DAO) 来实现 IoC,而 POJOEJB 使用 Hibernate 或 ORM 映射来实际处理数据库层。
因此,如果您希望您的业务逻辑不关心数据库正在使用、访问和更新的内容、内容和方式,并且您希望 DAO 负责此
DAO 可以支持更改不同表的逻辑以支持操作,方法是创建一个休眠调用的数量。
本质上,您通过在 DAO 和 Hibernate 两层中再次打破其功能,在数据访问层中实现分层方法。

于 2013-12-11T01:32:41.930 回答
1

如果您使用 ORM:享受他们的透明持久性支持!不要使用 DAO 来包装 ORM API。正如这里所说的,DAO 在 ORM 之前。ORMs 引入了来自 OODBMS 的概念,例如透明持久性和可达性持久性。你必须利用它,因为它会让你的生活更轻松,你的代码更漂亮。假设您正在为部门和员工建模...一个用例可能是创建一个新部门,创建一个新员工并将该员工添加到该部门...您会做什么?

//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

非常简单,这要归功于 Hibernate(与其他 ORM 一样)实现的透明持久性和可达性持久性。根本没有 DAO。

只需编写您的域模型,然后像在内存中一样思考。通过良好的映射策略,ORM 将透明地将你在内存中的内容持久化。

更多示例:http: //www.copypasteisforword.com/notes/hibernate-transparent-persistence http://www.copypasteisforword.com/notes/hibernate-transparent-persistence-ii

于 2016-04-16T14:20:25.123 回答
0

它实际上必须比所有这些答案都简单。这些模式都是关于层的。您不希望循环引用您创建只能了解其上方事物的图层。您希望您的 UICode 能够引用任何和所有服务,您的服务代码能够引用任何和所有 DAO。

  1. 服务
  2. 用户界面代码

POJO 从上到下传递。

于 2009-09-04T12:18:17.957 回答