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?
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.
You make some points. But I nevertheless use a Dao layer, here's why:
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).
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).
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:
The code above is as easy and simple as possible, and can be easily unit tested.
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.
The purpose of all this introduction to layers was to make maintainability easy and simple.
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.
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).