Abstracting the DAO Layer with Hibernate/JPA
A well-abstracted DAO layer means that the service layer doesn’t know anything of the persistence mechanism. This is generally desirable – you can later switch to a different storage – NoSQL, XML, etc. by only implementing a new DAO layer, and not touching anything else in the application.
Now, what would that look like:
- A set of DAO interfaces. Working with concrete DAO classes defeats the whole purpose of the dao being changeable
- A generic base DAO class to handle the CRUD operations
- Other common convenience methods, like such for executing queries. Those should be protected in the base class, so that they are not part of the public API
- A concrete DAO subclass for each special case – UserDaoImpl (implementing UserDao), ProductDaoImpl, etc.
And a typical service method may look like this (assuming all dependencies are obtained/injected):
public void modifyMessage(long messageId, String text, String editComment) {
Message message = messageDao.getById(messageId);
message.setText(text);
message.setNewDate(getCurrentDate());
message.setEditComment(editComment);
messageDao.save(message);
notifyFavourites(message); // notifies users that favourited this message that it has changed
}
This is completely straightforward. So far things are so abstract that it doesn’t matter whether it is Hibernate/JPA, JDBC, NoSQL, XML or binary serialization “behind” the DAO. Let’s now imagine it is JPA. There is a slight problem, but it is hard to spot – the line with messageDao.save(message)
is redundant. The message entity is in persistent state and it will be persisted at flush time even if we don’t call save
.
So removing that line, and all similar lines in our application would mean that we are working with JPA in a proper way, and not making redundant method calls that can even confuse newcomers to the project who might ask themselves “why is this needed here”.
This is an example of “The law of leaky abstractions” – we tried to make our service layer agnostic of the persistent mechanism, but the abstraction leaked through the state of the entities. And this can mean that no other persistence mechanism can be used if we make our application work properly with JPA persistence.
This is not good, but luckily we can retain the above line, perhaps with proper comment, or some general guideline documentation as of why are we explicitly calling save, although it doesn’t do anything. The line will do nothing in a JPA context, but will perform the actual save in, say, a NoSQL context. But if we omit it, then imagine deciding where to place a save-method call in an already big project.
So, when working with JPA, or whatever persistence mechanism that leaks in some way to the layer above the DAO (the service layer), make sure your service code will work with any other persistence mechanism.
A well-abstracted DAO layer means that the service layer doesn’t know anything of the persistence mechanism. This is generally desirable – you can later switch to a different storage – NoSQL, XML, etc. by only implementing a new DAO layer, and not touching anything else in the application.
Now, what would that look like:
- A set of DAO interfaces. Working with concrete DAO classes defeats the whole purpose of the dao being changeable
- A generic base DAO class to handle the CRUD operations
- Other common convenience methods, like such for executing queries. Those should be protected in the base class, so that they are not part of the public API
- A concrete DAO subclass for each special case – UserDaoImpl (implementing UserDao), ProductDaoImpl, etc.
And a typical service method may look like this (assuming all dependencies are obtained/injected):
public void modifyMessage(long messageId, String text, String editComment) { Message message = messageDao.getById(messageId); message.setText(text); message.setNewDate(getCurrentDate()); message.setEditComment(editComment); messageDao.save(message); notifyFavourites(message); // notifies users that favourited this message that it has changed }
This is completely straightforward. So far things are so abstract that it doesn’t matter whether it is Hibernate/JPA, JDBC, NoSQL, XML or binary serialization “behind” the DAO. Let’s now imagine it is JPA. There is a slight problem, but it is hard to spot – the line with messageDao.save(message)
is redundant. The message entity is in persistent state and it will be persisted at flush time even if we don’t call save
.
So removing that line, and all similar lines in our application would mean that we are working with JPA in a proper way, and not making redundant method calls that can even confuse newcomers to the project who might ask themselves “why is this needed here”.
This is an example of “The law of leaky abstractions” – we tried to make our service layer agnostic of the persistent mechanism, but the abstraction leaked through the state of the entities. And this can mean that no other persistence mechanism can be used if we make our application work properly with JPA persistence.
This is not good, but luckily we can retain the above line, perhaps with proper comment, or some general guideline documentation as of why are we explicitly calling save, although it doesn’t do anything. The line will do nothing in a JPA context, but will perform the actual save in, say, a NoSQL context. But if we omit it, then imagine deciding where to place a save-method call in an already big project.
So, when working with JPA, or whatever persistence mechanism that leaks in some way to the layer above the DAO (the service layer), make sure your service code will work with any other persistence mechanism.
A good tip !
Good post on proper DAO layer. I just gave a presentation to a group of .NET developers whose code had no such clean lines — although it did have “business layers” and “DALs” and “DTOs.” In name, only.
With regard to JPA saving the object on each method call, this is not really the norm, is it? Seems like JPA should at least track a “dirty” state and do nothing if you call the Save method (why is it even there?).
Another possible issue with an implicit “save object” on each property setter is concurrency — although it’s possible JPA addresses it: what happens if two threads update the message at the same time? (I know nothing about JPA…)
–Keith
@Keith here transactions come into the picture and it becomes a bit more complicated, because there is a choice of how to manage transactions. With spring and EJB they are usually container managed, so you just define them with annotations – i.e. “this service method is transactional – it is executed within a (new) transaction”. The choice of transaction management covers the concurrency issue – depending on the isolation level and on the locking of choice – pessimistic, optimistic.
Saving on each method call is not a norm, but saving on one “business call” (usually equal to the session lifetime and also to 1 transaction) is. And on session flush, if there is an object that has been associated with the session (entity manaager) (by either find, merge, persist,..), then its changes are automatically reflected in the database.