Why put a DAO layer over a persistence layer (like

2019-03-08 18:32发布

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?

10条回答
Anthone
2楼-- · 2019-03-08 19:24

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

查看更多
你好瞎i
3楼-- · 2019-03-08 19:27

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.

查看更多
倾城 Initia
4楼-- · 2019-03-08 19:34

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.

查看更多
你好瞎i
5楼-- · 2019-03-08 19:35

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.

查看更多
登录 后发表回答