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

发布于 2024-08-04 01:18:27 字数 840 浏览 2 评论 0原文

数据访问对象 (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对子对象的透明加载不是绕过了数据访问层吗?为了保持一致性,所有 BookDescription 对象是否都应该通过某些 BookDescriptionDao 对象(或 BookDao.loadDescription 方法)加载和保存?然而,以这种方式重构会使模型的操作变得不必要的复杂化。

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

Data Access Objects (DAOs) are a common design pattern, and recommended by Sun. But the earliest examples of Java DAOs interacted directly with relational databases -- they were, in essence, doing object-relational mapping (ORM). Nowadays, I see DAOs on top of mature ORM frameworks like JDO and Hibernate, and I wonder if that is really a good idea.

I am developing a web service using JDO as the persistence layer, and am considering whether or not to introduce DAOs. I foresee a problem when dealing with a particular class which contains a map of other objects:

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

JDO is clever enough to map this to a foreign key constraint between the "BOOKS" and "BOOKDESCRIPTIONS" tables. It transparently loads the BookDescription objects (using lazy loading, I believe), and persists them when the Book object is persisted.

If I was to introduce a "data access layer" and write a class like BookDao, and encapsulate all the JDO code within this, then wouldn't this JDO's transparent loading of the child objects be circumventing the data access layer? For consistency, shouldn't all the BookDescription objects be loaded and persisted via some BookDescriptionDao object (or BookDao.loadDescription method)? Yet refactoring in that way would make manipulating the model needlessly complicated.

So my question is, what's wrong with calling JDO (or Hibernate, or whatever ORM you fancy) directly in the business layer? Its syntax is already quite concise, and it is datastore-agnostic. What is the advantage, if any, of encapsulating it in Data Access Objects?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

愚人国度 2024-08-11 01:18:27

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

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

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

You make some points. But I nevertheless use a Dao layer, here's why:

  1. Database accesses are calls to a remote system. In all such cases (also web-service, ajax etc...), the granularity of interaction need to be big enough. Many tiny calls would kill performance. This performance necessity requires often a different view of the system, or layer (here, the Dao layer).

  2. Sometimes, your persistence operation is only to load/save/delete an object. One unique Dao (or a superclass ; consider Generics) can be responsible for this, so you don't have to code these methods again and again.
    But often, you also have specific needs, like running a specific request that is not automatically created by the ORM. There, you code your specific need with a specific Dao method (reuse is often possible).
    Having regular and specific needs in the same layer allow for reuse (for example, interception can ensure that a database connection is open/commited when needed).

墨小沫ゞ 2024-08-11 01:18:27

随着时间的推移,DAO 已经失去了意义。

在 J2EE 时代,当它成为一种流行模式时,DAO 是一个类,您可以在其中同时满足多个数据源的需求 - 一个供应商的数据库、另一个供应商的数据库、一个文件 - 并提供一个位置来包装查询进行数据通信。

有足够的重用范围,因此特定实体的 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() 并知道它在哪里持久化。

DAO has lost its meaning over time.

During the J2EE days when it became a popular pattern, a DAO was a class where you could simultaneously cater for multiple sources of data - a database by one vendor, a database by another, a file - and provide a single place to wrap queries to communicate for data.

There was plenty of scope for reuse, so an DAO object for a particular entity may well extend an abstract DAO which housed the re-usable stuff, which in itself implemented a DAO interface.

Post-J2EE/EJB, the DataMapper and DataSource patterns (or for simple systems, ActiveRecord) became popular to perform the same role. However, DAO became a buzzword for any object involved with persistence.

Nowdays, the term 'DAO' has sadly become a synonym for "a class which enables me to communicate with my database".

With ORM / JPA, much of the rationale for a true, J2EE era DAO is provided out of the box.

In the case of a latter DataSource pattern, JPA's EntityManager is akin to the DataSource, but is usually provided via a PersistenceUnit XML definition and instantiated via IoC.

The CRUD methods which once lived in a DAO or Mapper can now be provided exactly once using the Repository pattern. There's no need for AbstractDAO's - the ORM products are clever enough to accept an Object() and know where it is persisting it.

靖瑶 2024-08-11 01:18:27

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

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

It depends what your layer's goals are. You put an abstraction in to supply a different set of semantics over another set. Generally further layers are there to simplify somethings such as development of future maintennance. But they could have other uses.

For example a DAO (or persistence handling) layer over an ORM code supply specialised recovery and error handling functionality that you didn't want polluting the business logic.

挽清梦 2024-08-11 01:18:27

当使用 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);
    ...

上面的代码尽可能简单,并且可以轻松进行单元测试。

When using an ORM tool like JDO or JPA, DAOs are an anti-pattern. In this case, creating a "data access layer" is completely unnecessary and will only add extra code and complexity to the codebase, making it harder to develop and maintain.

Based on my previous experience, I would recommend the use of a simple static facade, say Persistence, to provide an easy to use, high-level API for persistence-related operations.

Then, you can use an static import to get easy access to those methods anywhere they are useful. For example, you could have code like the following:


    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);
    ...

The code above is as easy and simple as possible, and can be easily unit tested.

我还不会笑 2024-08-11 01:18:27

一个词:事务

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

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

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

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

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

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

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

One word: transactions

Take the situation where I have to perform two data update operations in a single transaction. These operations together form a logical unit of work. My business logic wants to express itself in terms of that unit of work, and it doesn't want to bother itself with transaction boundaries.

So I write a DAO. Take this pseudo code using Spring transactions and hibernate:

edited to remove HQL that was offending @Roger so much but which wasn't relevant to the point

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

My business logic calls doUnitOfWork(), which begins a transaction, performs both persistence operations, and then commits. It neither knows nor cares about the transaction, or what operations are performed.

Furthermore, if the DAO implements an interface with the doUnitOfWork() method, then the business logic can code to the interface, making it easier to unit test.

Generally, I always wrap my data access operations in a DAO, and whack an interface around it.

可是我不能没有你 2024-08-11 01:18:27

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

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

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

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

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

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

I believe most DAOs are added by people for histerical (historical ;] ) reasons. You are right in that they were intially meant as a convenient encapsulation of the SQL glue required to perform the CRUD operations in pre ORM days. Nowadays, with transparent persistence, their role is now largely redundant.

What is now appropriate is the concepts of Repositories and Services:

Repository:
A class that stores a collection of query methods implemented in ORM specific code (eg, Hibernate or JDO)

Typically you can create an abstract base class Repository and then provide an ORM specific implementation into which you implement all the query methods in code that is specific to your ORM. The great thing about this approach is that you can create a MockRepository implemenation to help test your app without using the DB.

Service:
A class that stores a collection of methods that can orchestrate non trivial changes/additions to the object model (typically ORM independent code).

This helps to keep your app largely ORM independent - to port the app to another ORM really only involves the implementation of a new ORM specific Repository class(es).

开始看清了 2024-08-11 01:18:27

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

I suppose that the pattern "DAO class per entity" is absolutely redundant for an ORM-managed data layer. Instead, the DAO layer should be composed of a set of one-fits-all CRUD method set that operate on arbitrary entity classes and a large number of methods that perform more sophisticated operations on data. If the functionality is large enough then the DAO layer should be split into multiple classes based on the domain criteria, what makes the approach more similar to the Service-Oriented Architecture.

铜锣湾横着走 2024-08-11 01:18:27

引入层的所有目的都是为了使可维护性变得简单。

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

第一层(数据访问层)的目的是处理数据库逻辑并防止业务层了解任何数据库详细信息。
数据访问层使用 POJO 或 EJB (DAO) 来实现 IoC,POJOEJB 使用 Hibernate 或 ORM 映射来实际处理数据库层。
所以,如果你希望你的业务逻辑不应该关心which、what&数据库如何被使用、访问和更新,您希望 DAO 处理这个问题
DAO可以通过多次hibernate调用来支持更改不同表的逻辑以支持操作。
本质上,您正在数据访问层中实现分层方法,方法是将其功能再次分为两层,即 DAO 和 Hibernate。

The purpose of all this introduction to layers was to make maintainability easy and simple.

  1. Data Access Layer
  2. Business Layer
  3. Presentation Layer

The purpose of the 1st Layer (Data Access Layer) is to deal with the database logic and prevent the Business Layer from knowing any of the DB details.
The Data Access Layer uses POJO or EJBs (DAO) to implement IoC and POJOEJBs uses Hibernate or ORM mapping to actually deal with the Database Layer.
So, if you want your business logic should not care about which, what & how a database is being used, accessed and updated and you want DAO to take care of this
DAO can support the logic of changing different tables to support operation by making a number of hibernate calls.
In essence, you are implementing a layered approach in Data Access Layer by breaking its functionality again in two layers aka DAO and Hibernate.

作妖 2024-08-11 01:18:27

如果您使用 ORM:享受他们的透明持久性支持!不要使用 DAO 来包装 ORM API。正如这里所说的,DAO 早于 ORM。
ORM 引入了 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

If you use an ORM: Enjoy their Transparent Persistence Support! Don't use DAOs to wrap ORM APIs. As it was well said here, DAOs are before ORMs.
ORMs has introduced concepts from OODBMS, like Transparent Persistence and Persistence by Reachability. You have to take advantage of that, because it will make your life easier and your code beautiful.
Suppose your are modeling departments and employees... One use case might be creating a new department, creating a new employee and adding the employee to the department... what you would do the?

//start persistence context
...
Department dept1 = new Department("department1");
dept1.addEmployee(new Employee("José", 10503f));

em.persist(dept1);
...
//close persistence context

Department, Employee and their relation are persistent now.

Suppose now you have to add an existing Employee to and existing Department... what you would do? pretty simple:

//start persistence context
...
Department aDepart = hibernateSession.load(Department.class, dId);
Employee anEmployee = hibernateSession.load(Employee.class, eId);

aDepart.addEmployee(anEmployee);     
...
//close persistence context

Pretty simple thanks to the Transparent Persistence and Persistence by Reachability that Hibernate (like other ORMs) implements. No DAOs at all.

Just code your domain model and think like you are persisting in memory. With a good mapping strategy, the ORM will transparently persist what do you in memory.

More examples here:
http://www.copypasteisforword.com/notes/hibernate-transparent-persistence
http://www.copypasteisforword.com/notes/hibernate-transparent-persistence-ii

萌吟 2024-08-11 01:18:27

它实际上一定比所有这些答案所显示的更简单。这些图案都是关于层次的。您不希望循环引用您创建的图层,这些图层只能了解其上方的内容。您希望您的 UICode 能够引用任何和所有服务,您的服务代码能够引用任何和所有 DAO。

  1. DAO
  2. 服务
  3. UICode

以及从上到下传递的 POJO。

It's actually must simpler than all of these answers make it out to be. These patterns are all about layers. You don't want circular references to you make layers that can only know about things above them. You want your UICode to be able to reference any and all Services, your Service code to be able to reference any and all DAOs.

  1. DAO
  2. Service
  3. UICode

with the POJOs being passed from top to bottom.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文