DAO和Repository模式有什么区别?

564

数据访问对象(DAO)和仓储库模式有何区别?我正在使用企业Java Beans(EJB3)、Hibernate ORM基础设施以及领域驱动设计(DDD)和测试驱动开发(TDD)作为设计技术开发应用程序。


Repository 是 Dao,因为它允许您访问/持久化数据,但是 Repository 基于模拟与数据集合的交互来定义更加精确。这个定义和预期的好处可以在 Eric Evans 的 DDD 中找到。 - Tristan
1
下面的答案中可以找到两种类型的解释:
  1. 它们在数据访问的抽象级别上有所不同;
  2. 它们完全没有区别,因为它们执行相同的任务,即隐藏访问数据的实现细节。
- Enfield Li
14个回答

647

DAO是数据持久化的抽象。
Repository是一组对象的抽象。

DAO与数据库更接近,通常以表为中心。
Repository更接近领域,仅处理聚合根。

Repository可以使用DAO实现,但反之则不行。

此外,Repository通常是较窄的接口。它应该只是一个对象集合,并提供Get(id)Find(ISpecification)Add(Entity)方法。

Update方法适用于DAO,而不适用于Repository- 使用Repository时,对实体的更改通常由单独的UnitOfWork跟踪。

似乎常见的实现被称为Repository,但实际上更像是DAO,因此我认为存在一些关于它们之间区别的困惑。


41
好的,您不会希望您的DAO类字面上实现您的IRepository接口。您希望您的存储库在其实现中使用DAO。请记住,DAO将是每个表对象,而存储库几乎总是需要使用多个DAO来构建单个实体。如果您发现不是这种情况,即您的存储库和实体仅需要访问单个表,则最有可能正在构建贫血的域模型。 - quentin-starin
37
我注意到在.NET世界中,术语“Repository”通常用于指代本质上等同于DAO的概念;而“DAO”更多地是Java的专业术语。 - Wayne Molina
21
DAO并不是按表来设计的,它只是抽象了对数据的访问方式——你可以根据自己的喜好来实现它(按表、按分组或模型)。推荐的做法是基于你的领域模型来塑造DAO,而不是考虑底层持久化,因为这样更容易/清晰地使用,并且给你更多的灵活性,比如,想象一下你需要一个将数据存储在XML文件中或从消息队列获取数据而不是从数据库中获取数据的DAO... - Stef
28
@Stef 我不同意。根据其定义(数据访问对象),DAO本质上返回数据。而仓储库按照其定义,返回领域对象。由此可以推断,仓储库应该使用DAO,而不是相反,因为在面向对象编程中,我们通过一个或多个数据对象来组合领域对象,而不是反过来。 - Mihai Danila
11
为什么代码库是“只读”概念,而DAO是“读写”概念? - Dennis
显示剩余21条评论

153

好的,我觉得我可以更好地解释我在评论中提到的内容:基本上,你可以把它们两个看作是一样的,虽然DAO是比Repository更灵活的一种模式。如果你想同时使用它们两个,你将在你的DAO中使用Repository。我将对它们各自进行解释:

REPOSITORY:

它是一个特定类型对象的存储库——它允许您搜索特定类型的对象以及存储它们。通常情况下,它只会处理一种类型的对象。例如,AppleRepository 将允许您执行 AppleRepository.findAll(criteria) 或者 AppleRepository.save(juicyApple)

请注意,Repository 使用的是领域模型术语(而不是数据库术语——与数据如何持久化无关)。

存储库很可能会将所有数据存储在同一个表中,但该模式并不要求如此。然而,它仅处理一种类型的数据,这使它逻辑上连接到一个主表(如果用于数据库持久性)。

DAO - 数据访问对象(换句话说,用于访问数据的对象)

DAO 是一个为您定位数据的类(主要是查找器,但通常也用于存储数据)。这种模式不限制你存储相同类型的数据,因此你可以很容易地拥有一个定位/存储相关对象的 DAO。

例如,您可以很容易地拥有一个 UserDao,该 UserDao 公开类似于:

Collection<Permission> findPermissionsForUser(String userId)
User findUser(String userId)
Collection<User> findUsersForPermission(Permission permission)

所有这些都与用户(和安全)有关,可以在同一个DAO中指定。这对于Repository来说并非如此。

最后

请注意,这两种模式实际上意思相同(它们存储数据并抽象出对数据的访问,并且它们都更接近领域模型并且几乎不包含任何数据库引用),但它们的使用方式可能略有不同,DAO更加灵活/通用,而Repository则更具特定性和限制性,只适用于某一类型。


@StefanFalk,看看Spring Data吧,它可以让你进行比那更好的调用。例如,这可以写成CarRepository.findByLanguageId(language.id),而且你甚至不需要编写代码,只需定义一个具有该名称方法的接口,Spring Data会为您构建默认类实现。相当不错的东西 ;) - Stef
这篇文章展示了如何在Spring Data中编写查询,但如果我使用Hibernate,我需要吗?还是我只需在另一个类中实现TodoReporitory,然后实现那些方法,比如MyRepository extends TodoRepository,其中MyRepository使用Hibernate进行持久化。网络上有太多的信息,我正在一个已经有较大数据库和服务器/客户端通信的项目中开始感到困惑。 - Stefan Falk
2
Spring Data 的美妙之处在于你实际上不需要编写查询语句,只需创建一个接口(就像你的示例中的 TodoRepository,它有一个名为 findById 的方法)。然后你就几乎完成了。Spring Data 会查找所有这些你已经创建的扩展 Repository 接口的接口,并为你创建类。你永远看不到这些类,也无法创建新实例,但你不需要这样做,因为你可以自动装配接口并让 Spring 定位那个仓库对象。 - Stef
1
最后,你不必使用Spring Data,你可以按照旧的方式自己编写查询方法(使用Criteria API等),但这只会让你的生活变得更加复杂...你可能会说这样做会让你拥有更多的灵活性,但事实并非如此,因为如果你真的想在查询方面发挥创意,Spring Data 允许你有两种方法:@Query注释,或者如果那不起作用,你可以创建自定义存储库,它是一个扩展,给你与从头开始编写自己的实现相同的能力。 - Stef
3
“Aggregated root” 是一个经常与仓储模式相关的术语。我不知道如何将其与您对仓储的定义结合使用。 - Christian Strempfer
显示剩余6条评论

133
DAO和Repository模式是实现数据访问层(DAL)的方式。所以,让我们先从DAL开始。
访问数据库的面向对象应用程序必须具备处理数据库访问的逻辑。为了保持代码的清晰和模块化,建议将数据库访问逻辑隔离到一个单独的模块中。在分层架构中,这个模块就是DAL。
到目前为止,我们还没有讨论任何具体的实现方式,只是一个将数据库访问逻辑放入单独模块的一般原则。
那么,我们如何实现这个原则呢?嗯,一种常见的实现方式,特别是在像Hibernate这样的框架中,就是DAO模式。
DAO模式是生成DAL的一种方式,通常每个领域实体都有自己的DAO。例如,User和UserDao,Appointment和AppointmentDao等。Hibernate中的DAO示例:http://gochev.blogspot.ca/2009/08/hibernate-generic-dao.html
那什么是仓储模式?就像DAO一样,仓储模式也是实现数据访问层(DAL)的一种方式。仓储模式的主要观点是,从客户端/用户的角度来看,它应该像一个集合一样。所谓像一个集合一样的行为,并不是指它必须像Collection collection = new SomeCollection()这样被实例化。相反,它意味着它应该支持添加、删除、包含等操作。这就是仓储模式的本质。
在实践中,例如在使用Hibernate的情况下,仓储模式是通过DAO来实现的。也就是说,DAL的实例既可以是DAO模式的实例,也可以是仓储模式的实例。
仓储模式不一定是建立在DAO之上的(正如一些人可能会建议的那样)。如果DAO被设计为支持上述操作的接口,那么它就是仓储模式的一个实例。想一想,如果DAO已经提供了类似集合的一组操作,那么还需要额外的层吗?

23
如果DAO已经提供了一组类似集合的操作,那么为什么还需要在其上面添加额外的层?假设你正在建模一个宠物店,并且你有一个“PetType”表,其中包含不同动物及其属性(名称:“猫”,类型:“哺乳动物”等),由另一个表“Pet”引用,该表记录你商店里的具体宠物(名称:“凯特尼斯”,品种:“三花猫”等)。如果你想添加一种尚未在数据库中出现过的动物类型,你可以使用存储库将创建PetType和Pet的两个单独的DAO调用(一个用于创建PetType,另一个用于Pet)分组在一个方法中,避免DAO之间的耦合。 - Matt

117
坦白地说,这看起来更像是语义上的区别而非技术上的区别。"数据访问对象"这个短语根本不涉及"数据库"。虽然你可以将其设计为以数据库为中心,但我认为大多数人会认为这样做是设计缺陷。
DAO的目的在于隐藏数据访问机制的实现细节,那么仓库模式有何不同呢?就我所知,它并没有不同之处。说"仓库模式"与"DAO"不同,因为你处理/返回对象集合是错误的,因为DAO也可以返回对象集合。
我读到的所有关于仓库模式的内容似乎都依赖于这种区别:糟糕的DAO设计与良好的DAO设计(即仓库设计模式)。

12
没错,完全同意,它们本质上是相同的。DAO听起来更像与数据库相关,但实际上并不是。就像Repository一样,只是一个用于隐藏数据位置和存储方式的抽象概念。 - Stef
2
对于这个语句点赞。坦白地说,这看起来更像是一个语义上的区别,而不是技术上的区别。数据访问对象这个短语根本不涉及“数据库”。 - Sudhakar Chavali
2
比较仓库(Repositories)和集合(collections)时,重点不在于它们处理/返回对象的集合,而在于仓库表现得好像它们本身就是集合一样。例如,在Java中,这意味着Repository没有更新方法,因为当您修改集合中的对象时,它会自动更新(因为Java集合仅存储对象的引用)。 - Christoph Böhme

18

15

一个DAO可以让从存储中获取数据变得更加简单,同时隐藏了丑陋的查询语句。

仓库(Repository)也处理数据并隐藏查询,但是它处理的是业务/领域对象

仓库将使用DAO从存储中获取数据,并使用这些数据来还原业务对象

例如,一个DAO可能包含以下方法 -

 public abstract class MangoDAO{
   abstract List<Mango>> getAllMangoes();
   abstract Mango getMangoByID(long mangoID);
}

一个仓库(Repository)可以包含像这样的一些方法 -

   public abstract class MangoRepository{
       MangoDao mangoDao = new MangDao;

       Mango getExportQualityMango(){

       for(Mango mango:mangoDao.getAllMangoes()){
        /*Here some business logics are being applied.*/
        if(mango.isSkinFresh()&&mangoIsLarge(){
           mango.setDetails("It is an export quality mango");
            return mango;
           }
       }
    }
}

这个教程帮助我轻松理解了主要概念。


8
关键区别在于,仓库处理聚合中的聚合根的访问,而DAO处理实体的访问。因此,仓库通常将聚合根的实际持久化委托给DAO。此外,由于聚合根必须处理其他实体的访问,因此它可能需要将此访问委托给其他DAO。

5

DAO提供了对数据库/数据文件或任何其他持久性机制的抽象,以便可以在不知道其实现细节的情况下操作持久层。

而在Repository类中,可以在单个Repository方法中使用多个DAO类来从“应用程序角度”完成操作。因此,不要在域层使用多个DAO,而是使用repository来完成操作。 Repository是一个层,可能包含一些应用逻辑,例如:如果数据可用于内存缓存,则从缓存中获取它;否则,从网络中获取数据并将其存储在内存缓存中以供下次检索。


5

仓库就是经过精心设计的 DAO。

ORM 是以表为中心,而不是 DAO。

在仓库中不需要使用多个 DAO,因为 DAO 本身可以与 ORM 仓库 / 实体或任何数据访问层提供程序执行完全相同的操作,不管汽车存储在哪里以及如何存储(1张表、2张表、n张表、半个表、Web 服务、一张表和一个 Web 服务等)。服务使用多个 DAO/仓库。

我的自己的 DAO,假设叫做 CarDao,只处理 Car DTO,我的意思是,只接受 Car DTO 作为输入,并且只返回 Car DTO 或 Car DTO 集合作为输出。

所以,就像仓库一样,DAO 实际上是业务逻辑的 IoC,允许持久化接口不被持久化策略或传统所威胁。DAO 既封装了持久化策略,也提供了与领域相关的持久化接口。仓库只是那些没有理解精确定义的 DAO 的人们用来指代 DAO 的另一个词。


首先,“ORM repositories/entities”?你的意思是ORM实体。没有ORM存储库的概念。其次,ORM通常仅处理实体,即领域模型。DAO直接处理表格并抽象数据访问。它们也返回实体。存储库是最高级别的抽象,提供集合接口以获取实体。DAO可以是存储库,即抽象实际存储引擎,为其提供接口,并提供(缓存)实体的集合视图。DAO可以使用ORM与数据库进行接口,并委托实体操作。 - Paul-Sebastian Manole
3
同意@brokenthorn的观点。他评论中最关键的一点是“仓库是最高级别的抽象”,当你想要保护你的领域代码免受底层数据库技术影响时,这种抽象就变成了必需品。ORM/适配器/数据库驱动程序概念往往会泄漏到DAO中。如果你的应用程序支持多个数据库技术,或者如果你不想让你的应用程序被锁定到一个数据库中,直接从领域模型中使用DAO是行不通的。 - Subhash

3
尝试确定在以下情况下DAO或Repository模式更适用: 假设您想为持久性机制提供统一的数据访问API,以访问各种类型的数据源,例如关系型数据库,LDAP,OODB,XML存储库和平面文件。
如果感兴趣,还可以参考以下链接:

http://www.codeinsanity.com/2008/08/repository-pattern.html

http://blog.fedecarg.com/2009/03/15/domain-driven-design-the-repository/

http://devlicio.us/blogs/casey/archive/2009/02/20/ddd-the-repository-pattern.aspx

http://en.wikipedia.org/wiki/Domain-driven_design

http://msdn.microsoft.com/en-us/magazine/dd419654.aspx


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接