Why put a DAO layer over a persistence layer (like JDO or Hibernate)
Asked Answered
P

10

34

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?

Promethean answered 4/9, 2009 at 10:26 Comment(3)
Thank you for the answers so far. I can see that in some cases the DAO pattern could solve an immediate need, for example when you need specialized code for object retrieval, error handling, etc. But in other cases, it's more of a theoretical debate (one person's "maintainability" is another person's "premature abstraction") without a definitive answer.Promethean
To give some background to the question, my interest in DAOs was originally as a means to solve an immediate problem, namely injecting dependencies into the objects loaded by JDO. But I have since found what I think is a better solution: JDO's addInstanceLifecycleListener() method.Promethean
A few months have passed...in the end I did end up introducing a data access layer on top of JDO in order to encapsulate security aspects (limiting which entities are visible or editable by the current user).Promethean
N
10

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.

Nolpros answered 4/9, 2009 at 10:31 Comment(0)
J
13

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

Jacobina answered 4/9, 2009 at 10:38 Comment(1)
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.Nusku
A
13

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.

Anurag answered 9/4, 2012 at 17:25 Comment(2)
+1 Thanks for cataloguing this family of alternative patterns (DAO, DataSource, ActiveRecord, Repository). Makes me wonder what the next big thing is going to be... :)Promethean
If I could fit it correctly, your idea is DAO's with modern ORM products is the wrong way in terms of definition of DAO pattern. Besides that you suggest combine Repository pattern with ORM tools which sounds fair enough for finding operations. But if there is a need to update operation which is not related with Repository pattern there must be an extra object like Abstract DAO (or whatever you call complicated facade etc.)Fillin
N
10

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.

Nolpros answered 4/9, 2009 at 10:31 Comment(0)
P
6

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.

Proserpina answered 4/9, 2009 at 12:9 Comment(12)
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.Promethean
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).Nusku
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.Nusku
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.Sassy
Addition: see this one for explanation why I heavily disagree: olivergierke.de/wordpress/2009/01/…Sassy
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).Nusku
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)Nusku
(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?Nusku
You can call DAO over ORM an anti-pattern, but in my experience mixing peristence code and transactions in with domaain logic is an anti-pattern.Taster
@HDave: you didn't understand; in the approach I described, there is no "persistence code" per se (except in the persistence facade, a single class), and no transaction-related code (except for a few centralized places). Application code contains only the domain/presentation logic (usually in separate layers), with very clean and simple code. And true unit tests can be easily written.Nusku
@Proserpina I'm late to this thread but in my book use of the JDO/JPA API to run queries is indeed "data access code". Yes I know this code is decoupled from the actual data source (eg: you could switch from MySQL to Postgres) but you are couple to JDO/API itself.Korwin
@Korwin Consider that a business service class (in Java, C#, etc.) calls a DAO method returning a list of PurchaseOrder objects, and then iterates it while computing the total purchase amount. You would agree that such Java/C# code is "business logic" code, right? If so, then why would rewriting it in JPA-QL suddenly turn it into business access code? To me, it would still be business logic code, even if written in an object-oriented DSL (JPA-QL, JDO-QL, etc.) instead of the host OO language.Nusku
A
6

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.

Aleedis answered 4/9, 2009 at 12:25 Comment(6)
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.)Nusku
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.Nusku
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.Aleedis
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.Nusku
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.Aleedis
The transaction boundaries should be specified by the service layer, not the persistence layer (aka DAO)Curcuma
A
3

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

Acrobatic answered 24/11, 2010 at 1:53 Comment(1)
Thanks for bringing this up. On the surface it's difficult to see how the Repository pattern actually differs from DAOs, but evidently there is some difference in purpose (see for example warren.mayocchi.com/2006/07/27/repository-or-dao )Promethean
C
3

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.

Crum answered 17/6, 2011 at 7:28 Comment(1)
I agree - the "DAO class per entity" should never be seen again except by the paleontologists who dig up remains of our civilization in the year 3056. Another similar concept is the 'DTO' (Data Transfer Object). With most ORMs providing attach/detach there's no reason why you can't use detached entity objects for data transfer. There's no need to write an extra class for every entity class just to 'transfer it' (eg., object marshalling, serialization to/from JSON etc.,). Some argue that DTOs shield you from model changes yet most model changes need the DTO to be updated anyway!!Acrobatic
C
3

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.

Chromatid answered 11/12, 2013 at 1:32 Comment(0)
L
1

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

Linchpin answered 16/4, 2016 at 14:20 Comment(0)
P
0

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.

Phallus answered 4/9, 2009 at 12:18 Comment(1)
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.Nusku

© 2022 - 2024 — McMap. All rights reserved.