Generics Hibernate – part I

In one of my last project I had to face a surely common situation, i.e. to write and implement a lot of DAO, using Hibernate, that where almost identical between them. Being absolutely lazy and routine-phobic, I begun putting all the common code in one class, and then trying to find a way to use this code from all the other classes with some form of inheritance. I ended up with what (I think) is a nice solution… Generics, of course!

First, a little background is needed. Between the many JPA annotations, there is a funny one named “MappedSuperclass”. Simply put, a class (abstract or concrete) annotated this way does not map to a table, but its sublcasses does – following the usual Java rules about inheritance.

Example:

@MappedSuperclass
public abstract class AbsDTO {

 protected long id;

 @Id
 @GeneratedValue(strategy = GenerationType.IDENTITY)
 @Column(unique = true, nullable = false)
 public long getId() {
  return id;
 }

 public void setId(long idParam) {
  id = idParam;
  }
}
@Entity
@Table(name = "REAL_DTO")
public class DTO extends AbsDTO {

	@Column(name = "MSG_ID")
	private String msgId;

        public String getMsgId() {
		return msgId;
	}

	public void setMsgId(String msgIdParam) {
		msgId = msgIdParam;
	}

}

With the above code, the DTO Entity will be mapped to the table REAL_DTO with two fields:
MSG_ID declared by itself, and id, inherited from the AbsDTO.
Another relevant knowledge for this post is that with Hibernate it is possible to do basic (and even not-so-basic) CRUD operations without a line of SQL – and that the Object-type management is completely done by Hibernate itself.
Example:

	public Long save(DTO myDTO) {
		Long toReturn = null;
		try {
			Session session = sessionFactory.openSession();
			Transaction tx = session.beginTransaction();
			toReturn = (Long) session.save(myDTO);
			tx.commit();
			session.close();
		} catch (HibernateException e) {
			logger.error(e.getMessage(), e);
		}
		return toReturn;
	}

Of course this is obvious; after all, what is Hibernate for? But the implication is that all the info that Hibernate needs to do its job with an entity-object are inside the object itself – myDTO, in the previous example.
Now, back to DAO topic, how all this can be useful? Let’s do it Generics-way.
Hibernate knows what table update looking at the type of the object is working with – at runtime. In the example, it knows it has to save data of myDTO in the table REAL_DTO because it is the table DTO is mapped to. But, what if we don’t know the actual type of the object at compile-time?
Well, to cut a long story short, the next snippet actually works:

	public Long save(T myDTO) {
		Long toReturn = null;
		try {
			Session session = sessionFactory.openSession();
			Transaction tx = session.beginTransaction();
			toReturn = (Long) session.save(myDTO);
			tx.commit();
			session.close();
		} catch (HibernateException e) {
			logger.error(e.getMessage(), e);
		}
		return toReturn;
	}

using the most-beloved T indicator for Generics
Now, a problem could arise when we want to retrieve something from database without specifying the type of the object we want to retrieve, but maybe just an identifier of the record:

public T findById(long id) {
		T toReturn = null;
		try {
			Session session = sessionFactory.openSession();
			Transaction tx = session.beginTransaction();
			toReturn = (T) session.get(type, id);
			tx.commit();
			session.close();
		} catch (NonUniqueResultException e) {
			logger.error(e.getMessage() + " thrown for id " + id);
		} catch (HibernateException e) {
			logger.error(e.getMessage(), e);
		}
		return toReturn;
	}

In that case, the magic word is type – but how could we set it? Pretty easily, for sure

public abstract class AbsDAO<T> {
 private Class<T> type;

public AbsDAO() {
		type = (Class) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];
	}

}

Let’s put everything together – we have almost done:

public abstract class AbsDAO<T> {

	private Logger logger = Logger.getLogger(MyDAO.class.getName());

	protected SessionFactory sessionFactory;

	private Class<T> type;

	/**
	 * Creates an instance of AbsDAO
	 * 
	 */
	public AbsDAO() {
              type = (Class) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];
              sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
	}

	/**
	 * Find a MyDTO by its id
	 * 
	 * 
	 * @param id
	 * @return
	 */
	@SuppressWarnings({ "unchecked", "boxing" })
	@Transactional(rollbackFor = Exception.class)
	public T findById(long id) {
		T toReturn = null;
		try {
			Session session = sessionFactory.openSession();
			Transaction tx = session.beginTransaction();
			toReturn = (T) session.get(type, id);
			tx.commit();
			session.close();
		} catch (NonUniqueResultException e) {
			logger.error(e.getMessage() + " thrown for id " + id);
		} catch (HibernateException e) {
			logger.error(e.getMessage(), e);
		}
		return toReturn;
	}

	/**
	 * List all MyDTOs
	 * 
	 * @return
	 */
	@SuppressWarnings("unchecked")
	@Transactional(rollbackFor = Exception.class)
	public List<T> list() {
		List<T> toReturn = null;
		try {
			Session session = sessionFactory.openSession();
			Transaction tx = session.beginTransaction();
			toReturn = session.createCriteria(type).list();
			tx.commit();
			session.close();
		} catch (HibernateException e) {
			logger.error(e.getMessage(), e);
		}
		return toReturn;
	}

	/**
	 * Save a MyDTO and return the generated id
	 * 
	 * @param myDTO
	 * @return
	 */
	@Transactional(rollbackFor = Exception.class)
	public Long save(T myDTO) {
		Long toReturn = null;
		try {
			Session session = sessionFactory.openSession();
			Transaction tx = session.beginTransaction();
			toReturn = (Long) session.save(myDTO);
			tx.commit();
			session.close();
		} catch (HibernateException e) {
			logger.error(e.getMessage(), e);
		}
		return toReturn;
	}

	/**
	 * Update a MyDTO and return the status of the operations
	 * 
	 * @param myDTO
	 */
	@Transactional(rollbackFor = Exception.class)
	public boolean update(T myDTO) {
		boolean toReturn = false;
		try {
			Session session = sessionFactory.openSession();
			Transaction tx = session.beginTransaction();
			session.update(myDTO);
			tx.commit();
			session.close();
			toReturn = true;
		} catch (HibernateException e) {
			logger.error(e.getMessage(), e);
		}
		return toReturn;
	}

	/**
	 * Delete a MyDTO and return the status of the operations
	 * 
	 * @param myDTO
	 * @return
	 */
	@Transactional(rollbackFor = Exception.class)
	public boolean delete(T myDTO) {
		boolean toReturn = false;
		try {
			Session session = sessionFactory.openSession();
			Transaction tx = session.beginTransaction();
			session.delete(myDTO);
			tx.commit();
			session.close();
			toReturn = true;
		} catch (HibernateException e) {
			logger.error(e.getMessage(), e);
		}
		return toReturn;
	}
}

I put the instantiation of sessionFactory in the constructor with a method that has been deprecated in Hibernate 4, but in my real projecyts I usually inject it with Spring @Autowired annotation.
The final touch, let’s put it at work

@Repository
public class DAO extends AbsDAO {

}

That’s really all: a concrete class that extends AbsDAO passing as generic type a concrete class that extends AbsDTO.

Last notes
While I would never use DAO and DTO as class names in my projects, I used those here to ease the reading.
I am not claiming to have created something absolutely new – I have just put together piece of information and experience kindly shared on internet by others.
I had taken most of what I know from the web, and still I am doing that for my personal improvement; I thought it was right time to give something back – hoping someone would find it useful.

The story continues here!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s