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层,这就是为什么:
数据库访问是对远程系统的调用.在所有这些情况下(也就是web服务,ajax等......),交互的粒度必须足够大.许多微小的电话会扼杀性能.这种性能必然要求系统或层(这里是Dao层)的不同视图.
有时,持久性操作仅用于加载/保存/删除对象.一个独特的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()并知道它在哪里持久化.
Pre*_*gha 10
这取决于你的图层的目标是什么.你把一个抽象放在另一个集合上提供一组不同的语义.通常还有其他层来简化某些事情,例如未来维护的发展.但他们可以有其他用途.
例如,ORM代码上的DAO(或持久性处理)层提供了您不希望污染业务逻辑的专用恢复和错误处理功能.
使用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.使用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中,然后敲击它周围的界面.