数据访问对象(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;
}
JDO足够聪明,可以将其映射到“BOOKS”和“BOOKDESCRIPTIONS”表之间的外键约束。它透明地加载BookDescription对象(我相信使用延迟加载),并在Book对象被持久化时保留它们。
如果我要介绍一个“数据访问层”并编写一个像BookDao这样的类,并将所有JDO代码封装在其中,那么这个JDO透明加载的子对象不会绕过数据访问层吗?为了保持一致性,不应该通过一些BookDescriptionDao对象(或BookDao.loadDescription方法)加载和保存所有BookDescription对象吗?然而,以这种方式进行重构将使得模型操作变得不必要地复杂化。
所以我的问题是,在业务层直接调用JDO(或Hibernate,或者你想要的任何ORM)有什么问题?它的语法已经非常简洁,并且与数据存储区无关。如果有的话,将它封装在数据访问对象中有什么好处?
答案 0 :(得分:12)
你提出了一些观点。但我仍然使用Dao层,这就是原因:
数据库访问是对远程系统的调用。在所有这些情况下(也就是web服务,ajax等......),交互的粒度必须足够大。许多微小的电话会扼杀性能。这种性能必然要求系统或层(这里是Dao层)的不同视图。
有时,持久性操作只是加载/保存/删除对象。一个独特的Dao(或超类;考虑泛型)可以对此负责,因此您不必一次又一次地编写这些方法。
但通常情况下,您还有特定需求,例如运行不是由ORM自动创建的特定请求。在那里,您使用特定的Dao方法编写您的特定需求(通常可以重复使用)
在同一层中具有常规和特定需求允许重用(例如,拦截可以确保在需要时打开/提交数据库连接)。
答案 1 :(得分: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()并知道它在哪里持久化。
答案 2 :(得分:10)
这取决于你的图层的目标。你把一个抽象放在另一个集合上提供一组不同的语义。通常还有其他层来简化某些事情,例如未来维护的发展。但它们还有其他用途。
例如,ORM代码上的DAO(或持久性处理)层提供了您不希望污染业务逻辑的专用恢复和错误处理功能。
答案 3 :(得分: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);
...
上面的代码尽可能简单易用,并且可以轻松进行单元测试。
答案 4 :(得分:6)
一个字:交易
采取我必须在单个事务中执行两个数据更新操作的情况。这些操作共同构成了一个逻辑工作单元。我的业务逻辑想要用这个工作单元来表达自己,并且它不想打扰自己的事务边界。
所以我写了一个DAO。使用Spring事务和休眠来获取这个伪代码:
编辑删除那些冒犯@Roger但与点无关的HQL
@Transactional
public void doUnitOfWork() {
// some persistence operation here
// some other persistence operation here
}
我的业务逻辑调用doUnitOfWork(),它开始一个事务,执行持久性操作,然后提交。它既不知道也不关心交易,或者执行什么操作。
此外,如果DAO使用doUnitOfWork()方法实现接口,那么业务逻辑可以编码到接口,从而更容易进行单元测试。
通常,我始终将我的数据访问操作包装在DAO中,然后敲击它周围的界面。
答案 5 :(得分:3)
我相信大多数DAO都是由人们因为历史(历史)原因而添加的。你是对的,因为它们实际上意味着在ORM前几天执行CRUD操作所需的SQL粘合的方便封装。如今,凭借透明的持久性,他们的角色现在基本上是多余的。
现在适当的是存储库和服务的概念:
存储库: 存储在ORM特定代码(例如,Hibernate或JDO)中实现的查询方法集合的类
通常,您可以创建一个抽象基类Repository,然后提供一个ORM特定的实现,您可以在其中实现特定于ORM的代码中的所有查询方法。这种方法的好处在于您可以创建MockRepository实现来帮助测试您的应用程序而无需使用数据库。
服务: 一个类,它存储一组方法,这些方法可以协调对象模型的非平凡更改/添加(通常是ORM独立代码)。
这有助于保持您的应用程序在很大程度上独立于ORM - 将应用程序移植到另一个ORM实际上只涉及实现新的ORM特定存储库类。
答案 6 :(得分:3)
我认为对于ORM管理的数据层,“每个实体的DAO类”模式绝对是多余的。相反,DAO层应该由一组一对一的CRUD方法集组成,这些方法集对任意实体类进行操作,并且有大量方法对数据执行更复杂的操作。如果功能足够大,则应根据域标准将DAO层拆分为多个类,这使得该方法更类似于面向服务的体系结构。
答案 7 :(得分:3)
所有这些介绍层的目的是使可维护性简单易行。
第一层(数据访问层)的目的是处理数据库逻辑并防止业务层知道任何数据库细节。
数据访问层使用POJO或EJB(DAO)来实现IoC,POJOEJB使用Hibernate或ORM映射来实际处理数据库层。
所以,如果你想要你的业务逻辑不应该关心哪个,什么&amp;如何使用,访问和更新数据库,并希望DAO处理这个问题
DAO可以通过进行多次休眠调用来支持更改不同表的逻辑以支持操作
实质上,您通过在两层(即DAO和Hibernate)中再次破坏其功能,在数据访问层中实现分层方法。
答案 8 :(得分:1)
如果您使用ORM:享受透明持久性支持!不要使用DAO来包装ORM API。正如在这里所说的那样,DAO在ORM之前。 ORMs引入了OODBMS的概念,如透明持久性和可持续性持久性。你必须利用它,因为它会让你的生活更轻松,你的代码更美观。 假设您是建模部门和员工......一个用例可能是创建一个新部门,创建一个新员工并将该员工添加到部门......您会做什么?
import subprocess
with open(file, 'r') as myfile:
data=myfile.read().replace('\n', '')`
部门,员工及其关系现在都很持久。
假设您现在必须将现有员工添加到现有部门......您会做什么?非常简单:
//start persistence context
...
Department dept1 = new Department("department1");
dept1.addEmployee(new Employee("José", 10503f));
em.persist(dept1);
...
//close persistence context
非常简单,感谢Hibernate(像其他ORM)实现的透明持久性和可持续性持久性。根本没有DAO。
只需对您的域模型进行编码,并认为您在内存中持久存在。通过良好的映射策略,ORM将透明地保留您在内存中的内容。
此处有更多示例: http://www.copypasteisforword.com/notes/hibernate-transparent-persistence http://www.copypasteisforword.com/notes/hibernate-transparent-persistence-ii
答案 9 :(得分:0)
它实际上必须比所有这些答案都简单明了。这些模式都是关于层的。你不希望循环引用你创建只能知道它们上面的东西的图层。您希望您的UICode能够引用任何和所有服务,您的服务代码能够引用任何和所有DAO。
POJO从上到下传递。