Wednesday, September 26, 2007

Adding Generics to the AbstractHibernateDao

In my previous post, "The Best AbstractHibernateDao Ever", I made a passing reference to the generics being a problem.

So now, in slight contradiction to my "The Best Generic Dao Interface Ever" article, I am going to add Generics to the "AbstractHibernateDao".

If you don't know what Generics are, and you're a Java developer, you obviously aren't keeping up-to-date with your chosen trade. In fact, you should stop reading my blather and go study. Start here "New Features and Enhancements J2SE 5.0". Be sure to read the "Generics Tutorial"

Alrighty then. Why do we want generics on our DAO? Let's look back at our UserDao example, and have a look specifically at our "findAll" method...

public List<User> findAll() {
return all();
}

The problem with this method is in it's definition on the AbstractHibernateDao. The "all()" method comes from the AbstractHibernateDao and is defined like...

protected List all() {
return criteria().list();
}


Why are generics a problem here? Well the Hibernate critria returns a "List". It is a list of "Object"s, nothing more. Our AbstractHibernateDao respects that and returns a "List". This "List" is a "List<Object>" (a "List of objects"). Our UserDao on the other hand returns a "List<User>" (a "List of Users").

Well now we have a mismatch, a "List of Objects" is not a "List of Users", we are implying specifics that aren't enforced. Unfortunately there isn't much we can do about it. Hibernate doesn't have generics, so we have to have some faith that when we ask Hibernate for Users, it's going to give Users, not Toast. The easy fix for that is to mark our "UserDaoImpl.findAll()" method with the SuppressWarnings Annotation...

//fixin it the lazy way
@SuppressWarnings("unchecked")
public List findAll() {
return all();
}

That fixed it right? Wrong. This sucks. This sucks because I'll need to put this @SuppressWarnings annotation all over the place. I need it on each on all of the methods that return lists. I'll need it on most of the methods in every Dao I create. So like I said, this sucks.

OH! And don't forget about all the downcasting we're doing...

public User findByUsername(String username) {
return (User) criteria().add(
Restrictions.eq("username", username)
).uniqueResult();
}

Here, we are downcasting the "Object" returned from "uniqueResult()" to the "User" instance we asked for. Generics can help with all this.

My goal is for the DaoImpls to be "downcast" and "SuppressWarnings" free. In order to accomplish this I need to push the "dirty" stuff up into the AbstractHibernateDao. So I'll add a few wrapper methods that handle the downcasting and untyped collections...

public abstract class AbstractHibernateDao<E> {

private final Class<E> entityClass;
private final SessionFactory sessionFactory;

public AbstractHibernateDao(
Class<E> entityClass,
SessionFactory sessionFactory) {

Assert.notNull(entityClass,
"entityClass must not be null");
Assert.notNull(sessionFactory,
"sessionFactory must not be null");

this.entityClass = entityClass;
this.sessionFactory = sessionFactory;
}

protected Criteria criteria() {
return currentSession().createCriteria(entityClass);
}

protected Query query(String hql) {
return currentSession().createQuery(hql);
}

protected Session currentSession() {
return sessionFactory.getCurrentSession();
}

protected List<E> all() {
return list(criteria());
}

public Class<E> getEntityClass() {
return entityClass;
}

/*=== BEGIN GENERICS SUPPRESSION WRAPPERS ===*/

@SuppressWarnings("unchecked")
protected List<E> list(Criteria criteria) {
return criteria.list();
}

@SuppressWarnings("unchecked")
protected List<E> list(Query query) {
return query.list();
}

@SuppressWarnings("unchecked")
protected E uniqueResult(Criteria criteria) {
return (E) criteria.uniqueResult();
}

@SuppressWarnings("unchecked")
protected E uniqueResult(Query query) {
return (E) query.uniqueResult();
}

@SuppressWarnings("unchecked")
protected E get(Serializable id) {
return (E) currentSession().get(entityClass, id);
}
}


So, now, I've added generics to the AbstractHibernateDao. I've changed the class declaration to...
public abstract class AbstractHibernateDao<E>

Let's just jump right into the changes this makes to our UserDaoImpl...

public class UserDaoImpl extends AbstractHibernateDao<User> implements UserDao {

public UserDaoImpl(SessionFactory sessionFactory) {
super(User.class, sessionFactory);
}

public User findById(Long id) {
return get(id);
}

public User findByUsername(String username) {
return uniqueResult(criteria().add(
Restrictions.eq("username", username)
));
}

public List<User> findByEmail(String email) {
return list(query("from User u where u.email = :email")
.setParameter("email", email)
);
}

public List<User> findAll() {
return all();
}

public void save(User user) {
currentSession().saveOrUpdate(user);
}

public void delete(User user) {
currentSession().delete(user);
}
}

By employing Generics on the AbstractHibernateDao and isolating all downcasting and warning suppression to AbstractHibernateDao we can have a much cleaner DaoImpl. What I did was add wrapper methods to the AbstractHibernateDao for list(Criteria), list(Query), uniqueResult(Criteria), and uniqueResult(Query).

Using these new wrapper methods you see that the UserDaoImpl no longer calls query.list() it calls list(query) to get back a typed list. Also, the UserDaoImpl no longer calls criteria.uniqueResult(), it calls uniqueResult(criteria).

The isolation gives us one place to hide our dirty laundry (the AbstractHibernateDao). Maybe, some day, Hibernate will support Generics. That day is probably very, very far away. I would have thought the new JPA EntityManager API would support generics. Apparently it does not either, how unfortunate.

Monday, September 24, 2007

The Best AbstractHibernateDao Ever

(Follow up to "The Best Generic DAO Interface Ever")

I love the 3rd grade title theme I got going on here. Anyway...

I've seen so many incarnations of an AbstractHibernateDao out there; some are good, some are bad. Myself, I've always gone the AbstractHibernateDao extends HibernateDaoSupport route myself. I'm a huge fan of Spring for the amount of helpful stuff it provides in all areas of "Enterprisey Software Development".

I've done some re-thinking of the Spring Dao concept lately. See, according to Alef Arendsen of Interface21, "start using the Session and/or EntityManager API directly". In other words, stop using the HibernateTemplate, it isn't really useful.

As it turns out in Spring 2.x in combination with Hibernate 3.x (I'd go with no less than 3.2.1), you don't need the HibernateTemplate. If you are using the Spring LocalSessionFactoryBean to configure your Hibernate SessionFactory, the HibernateTemplate just isn't needed. The reason is that the LocalSessionFactoryBean creates a proxy SessionFactory that implements the SessionFactory.getCurrentSession() method appropriately for Spring intercepted classes.

So if you're using Spring Transaction management (via annotations, declarative xml, or what-have-you), you don't need the HibernateTemplate. Now, the one area that the HibernateTemplate does help with is the Exception translation. Hibernate 2.x just threw HibernateException for everything, you were left guessing really what the true problem was. The Spring HibernateTemplate would translate these 'bad' HiberatenExceptions into Spring's DataAccessException hierarchy. This meant that you could easily handle DataIntegrityViolationException vs. IncorrectResultSizeDataAccessException. Well, Hibernate 3.x has it's own exception hierarchy. So you can handle ConstraintViolationException vs. NonUniqueObjectException.

If you want that exception translation, there's some voodoo you can do with an annotation called "@Repository" (There's that repository word I mentioned in my last post). This annotation wraps your Dao (or Repository) with a proxy that will convert the HibernateExceptions to DataAccessExceptions. I don't think it's really that useful to do, so I don't do it...

So, now, if we combine Spring's Handling of the Session for us, with the Hibernate SessionFactory.getCurrentSession() we get a very simple, very clean AbstractHibernateDao to base our Dao's from...


public abstract class AbstractHibernateDao {

private final Class entityClass;
private final SessionFactory sessionFactory;

public AbstractHibernateDao(
Class entityClass,
SessionFactory sessionFactory) {

Assert.notNull(entityClass,
"entityClass must not be null");
Assert.notNull(sessionFactory,
"sessionFactory must not be null");

this.entityClass = entityClass;
this.sessionFactory = sessionFactory;
}

protected Criteria criteria() {
return currentSession().createCriteria(entityClass);
}

protected Query query(String hql) {
return currentSession().createQuery(hql);
}

protected Session currentSession() {
return sessionFactory.getCurrentSession();
}

protected List all() {
return criteria().list();
}

protected Object get(Serializable id) {
return currentSession().get(entityClass, id);
}

public Class getEntityClass() {
return entityClass;
}
}


The purpose of the AbstractHibernateDao above is to take away any work the sub-class Dao might have to do regarding the "Persistent Class" it's responsible for. Meaning that subclasses don't have to pass the Class all the time.

What does it look like in use? Well, let's pretend we have a User entity and a UserDao.

First, our "User" entity...

public class User {
private Long id;
private String username;
private String email;

//getters and setters omitted
}


And now the User Dao or Repository...

public interface UserDao extends Dao {

User findById(Long id);

User findByUsername(String username);

List findByEmail(String email);

List findAll();

void save(User user);

void delete(User user);
}


Now let's implement that UserDao using our AbstractHibernateDao...

public class UserDaoImpl extends AbstractHibernateDao implements UserDao {

public UserDaoImpl(SessionFactory sessionFactory) {
super(User.class, sessionFactory);
}

public User findById(Long id) {
return (User) get(id);
}

public User findByUsername(String username) {
return (User) criteria().add(
Restrictions.eq("username", username)
).uniqueResult();
}

public List findByEmail(String email) {
return query("from User u where u.email = :email")
.setParameter("email", email)
.list();
}

public List findAll() {
return all();
}

public void save(User user) {
currentSession().saveOrUpdate(user);
}

public void delete(User user) {
currentSession().delete(user);
}
}


If you're curious about what the "Dao" interface here looks like, see my previous post "The Best Generic Dao Interface Ever". Or you can skip it and just know that the Dao interface is simply a Marker, there isn't a single method on it.

Understanding this Dao infrastructure has a very low barrier to entry. You don't need to know how the Spring HibernateTemplate interacts with the Hibernate Session to use this. You only need to know how to use the Hibernate Session. There is very little noise in this Dao, it is all directly related to querying Hibernate, as it should be.

This implementation relies on the Hibernate SessionFactory.getCurrentSession() method. When you combine Spring with Hibernate here you get a very elegant solution with no stuff about transactions or any weird abstraction layer in the way. There are whole books around using the Hibernate Session. The Spring HibernateTemplate only gets a few pages dedicated to it in any Spring book. That right there means you spend less time explaining it to people, tell them to "Read the freakin' manual".

Disclaimer: You might notice that there are some generics used in here that will cause "unchecked" warnings. I didn't bother with a lot of generics and the @SuppressWarnings("unchecked") stuff in this example because they can be distracting. To see the generics cleaned up, read "Adding Generics to the AbstractHibernateDao"

Wednesday, September 12, 2007

The Best Generic Dao Interface Ever

If you Google the phrase "Generic Dao" or "Generified Dao", you'll find several hits all over the internet. They all look like this...

/**
* This sucks, keep reading...
*/
public interface Dao<I extends Serializable, E> {
List<E> loadAll();
E findById(I id);
void save(E entity);
void remove(E entity);
}

Now, the code above might not even compile, I just slapped it in. The purpose of this interface, and all the examples on the internet, is to define the overall structure of all Daos in the application. This generic Dao interface defines the following contract...
* All Daos will have a loadAll() that returns a list of entity instances.
* All Daos will have a findById method to find an entity with a given "id".
* All Daos will have a save and remove method that takes an entity.

Well isn't this just grand, we now know these four facts about all our Daos. Life is good and we'll all be paid huge for knowing this, right? Not really, the thing with this interface is that you've painted yourself into quite the corner. As your application grows you may not 'need' these methods. Even worse, you might come across cases where you simply shouldn't have these methods. Next thing you know you're trying to split things up into a "ReadOnlyDao" and a "WriteableDao" or some such garbage.

This is the case I found myself in. What did I do on my next project, after learning my lesson?

Try to keep up, this is going to be complex and probably way of the top in the 'voodoo' department. Are you ready?
Ok, here it is...

public interface Dao {
}

Look out ma! It's a marker interface! You may say, "But Ray, won't chaos ensue? We haven't defined the contrat that all our Daos should follow." Calm down. Do you really think you know how every Entity in your application should be accessed now? I mean if you're writing the "Dao" interface, you're probably pretty early in the project. You really have no idea what all the data access requirements are going to be.

In many systems every Entity has a Repository. Repository is a term gaining popularity over the Dao acronym. Repository is a nice word I guess, but we all call them Daos; so let's just go with it. Each Entity has a Dao. Let the Entity drive the Dao design. Let your domain drive the Dao design. For god's sakes though, don't get on a high horse and try to define it for them with some junk you found on the internet.

Now, the generified version does give you the ability to say that all our methods that destroy Entity instances should be called remove and not destroy on some Daos and delete on others. This rigid construct ends up in place when you may not want a remove method at all. Use something like CheckStyle, or at least have people on your team competent enough to follow the conventions set forth in your project.

Next, we'll talk about "The Best AbstractHibernateDao Ever".