views:

1822

answers:

5

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?

+4  A: 

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.

Preet Sangha
+6  A: 

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

KLE
About point 1: DAOs are not needed for that, not at all. About point 2: DAOs not needed for this too; I have coded hundreds of specific queries without making use of DAO classes, and the query methods were reusable.
Rogerio
A: 

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.

Rogerio
I'm happy to see you call DAOs an anti-pattern! But...isn't your static Persistence facade conceptually much the same thing as a DAO? I don't see the benefit of abstracting one-line JDO methods to one-line static methods, plus the abstraction is "leaky" because it requires one to use the query language of the underlying ORM.
Todd Owen
It's different, because conceptually a Facade is a simplified front for a larger, more complex API; which is exactly the case with the Hibernate, JPA, and JDO APIs.Those methods aren't really one-liners. They have to also open/obtain the proper work unit object (Hibernate Session, JPA EntityManager) from a ThreadLocal (in a web app); there may be some exception handling code; and so on.I don't mind the query language being exposed, because the real objective is to simplify client code, not allow portability. But I would recommend avoiding HQL, now with the standard JPA QL (or JDO QL).
Rogerio
Note also that the goal of a DAO is to encapsulate "data access" code. This makes sense with JDBC, but not with high-level ORM APIs which themselves encapsulate the real data access code. That's why I say DAO is an anti-pattern when using JDO or JPA.
Rogerio
DAO an antipattern? How do you unit test your client (service?) logic then? Really want to pollute this with JPA query strings? What about applied pagination, sorting? I really don't want to bother a service with that tasks.
Oliver Gierke
Addition: see this one for explanation why I heavily disagree: http://www.olivergierke.de/wordpress/2009/01/se-radio-episode-121-or-mappers/
Oliver Gierke
When using ORM, yes, DAO is an anti-pattern.As I said in my answer unit testing is not an issue, since there are good mocking tools that can be used for that.JPA queries (in strings or not) are not "data access code", if that's what you are thinking; they are business logic code. And, in my experience, using strings in code is the most effective way to write queries.Pagination and sorting usually are UI concerns, to be dealt with in UI code (although the use of "order by" is a good optimization).
Rogerio
Thanks for the link, I read the article. It does not present very good arguments, though:1. "Separation of persistence logic and business logic": the persistence facade I proposed does that, doesn't it? Unless you think that "select b from Book where b.price < ?" is persistence logic.2. "Tangled code / More sophisticated API": the persistence facade is a very good solution for this, from my experience; it can deal nicely with paging, for example.3. "Exchange implementation": this is totally bogus; unit testing does not require this, nor would it be an effective solution.(continues below)
Rogerio
(continued from above)4. "Don’t repeat the DAO!": I never saw such repeated code using a persistence facade; if you have a query that is useful in multiple use cases, then put it in a separate method.That was it, I think. Did I miss any other reason to use DAOs on top of an ORM API?
Rogerio
A: 

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.

andersonbd1
But what does this has to do with using DAOs or not? Layering and DAOs are independent concepts, even though DAOs are usually put in a dedicated layer.
Rogerio
+1  A: 

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.

skaffman
DAOs should not have transaction-related code, except for special situations where the common transaction demarcation rule does not apply. (Neither should business logic contain such code, of course.)
Rogerio
I disagree profoundly.
skaffman
Code like "getHibernateTemplate().execute("some HQL here");" is awful. It's verbose, and exposes the fact that Hibernate is used (which should really just be an implementation detail, now that we have JPA).Creating extra interfaces just for testing is an obsolete practice. I can unit test any kind of Java code just fine without them, with short and elegant JUnit/TestNG tests.
Rogerio
OK, I've removed the HQL, it wasn't relevant, the point was to encapsulate the unit of work. And just because JMockit doesn't need interfaces, the fact remains that mocking out interfaces is standard practice.
skaffman
Sorry if I sounded aggressive...To be more clear, though, I wasn't referring to the HQL code, but to the "getHibernateTemplate" method. I know this if from the Spring framework, but I could never understand why someone would use that: it's a poor, low-level, and redundant abstraction for a persistence service. A persistence facade (static or not) should at least hide the specific ORM API used under the covers, even if it can't hide the query language.
Rogerio
There's always the JPA API if that's your thing, but I find it crude and inexpressive. The irony of this tit-for-tat is that none of it is relevant to the OP's question.
skaffman