Generics with Hibernate – part II

In the previous post I have shown how to couple Hibernate and Generics to write all the code needed for CRUD operations in (basically) one class.
Forgive me a little digression. Few months ago I was working on a project (written by someone else, of course); one of the admin recreated the database we where using on a different place and: BANG! the application stopped working. To cut a long (and sad) story short, the previous developer have had the brilliant idea to put a lot of triggers inside the database to store backup data during CRUD operations. But when the admin recreated the database, he used a different “user”, so all the nice triggers previously created could not be accessed by the application, and it stop working.
My opinion on this story is that we are developer, and all the database work needed for the business domain of our application should be driven by our code – otherwise who can tell what could happen?
Now, in one of my project the data versioning, I mean keeping track of all the modifications done to the stored record, was part of the application domain, so I begun to search I smart way to do that (of course, without triggers!).
You know what, that was how I begun to implement the Generic Hibernate access I have shown in the previous post.
But now, stop chit-chat, and do some real work.
Basically, we have an entity, for example a customer, with a property, let’s say his mood, that may change over time, but we want to keep track of all his moods, and when he changed it. Of course, the easiest way would be to just save all modifications in the same table, but then a couple of problem arise:
1) maybe some part of data can not change: for example our customer has always the same name, but with this solution we would copy again and again the same information – useless;
2) most time we would need to access just the current customer state, but the table would be bigger because it holds also all the previous ones, so access would be slower.
A different approach could be to have two entities, one representing the current state and one representing the previous (let’s call it “version”) state. The minimum set of properties needed are the ids and the creation/modification times:

@MappedSuperclass
public abstract class AbsCurrentDTO {

	protected long id;
	protected Date creationDate;

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

	public void setId(long idParam) {
		id = idParam;
	}

	@Column(name = "CREATION_DATE", nullable = false)
	public Date getCreationDate() {
		return creationDate;
	}

	public void setCreationDate(Date creationDateParam) {
		creationDate = creationDateParam;
	}
}

@MappedSuperclass
public abstract class AbsVersionDTO {

	protected long id;
	protected Date modificationDate;
	protected long currentId;

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

	public void setId(long idParam) {
		id = idParam;
	}

	@Column(name = "MODIFICATION_DATE", nullable = false)
	public Date getModificationDate() {
		return modificationDate;
	}

	public void setModificationDate(Date modificationDateParam) {
		modificationDate = modificationDateParam;
	}

	public long getCurrentId() {
		return currentId;
	}

	public void setCurrentId(long currentIdParam) {
		currentId = currentIdParam;
	}

Here, currentId is needed to link all the VersionsDTO belonging to the same CurrentDTO. Remember, @MappedSuperclass here means that this classes would not be mapped to any real table.
Here’s the concrete entities:

@Entity
@Table(name = "CUSTOMER_CURRENT")
public class CustomerCurrentDTO extends AbsCurrentDTO {

	protected String name;
	protected String mood;

        @Column(name = "NAME", nullable = false)
	public String getName() {
		return name;
	}

	public void setName(String nameParam) {
		name = nameParam;
	}

	@Column(name = "MOOD", nullable = true)
	public String getMood() {
		return mood;
	}

	public void setMood(String commentParam) {
		mood = commentParam;
	}
}

@Entity
@Table(name = "CUSTOMER_VERSION")
public class CustomerVersionDTO extends AbsVersionDTO {

	protected String mood;

	public CustomerVersionDTO() {
		super();
	}

	public CustomerVersionDTO(CustomerCurrentDTO customerCurrentDTO) {
		currentId = customerCurrentDTO.getId();
		mood = customerCurrentDTO.getMood();
	}

	@Column(name = "MOOD", nullable = true)
	public String getMood() {
		return mood;
	}

	public void setMood(String commentParam) {
		mood = commentParam;
	}
}

As you see, ids and dates are not here, because they are inherited from parents. Customer define a couple of properties, name and mood; name is the one that never change, and mood is the changing one. Version define only the changing one, mood. Of course, this two entities are mapped to different tables.
While implementation of Current customer does not have anything fancy in it, something is going on with the Version.
There are two constructors, the default one and the other that accept CustomerCurrentDTO as parameter. That way, we could copy variable states from the Current to the Version entity in the constructor. The default constructor is neede by Hibernate as we will see later.
All this talking of Current and Version is nice and good, but what about DAO ?
Well, we just need to add some modifications to the one I have illustrated in the other post.
First, we have to pass both type to the AbstractDAO:

public abstract class AbsDAO<T, E> {

	private Class currentType;
	private Class versionType;

	public AbsDAO() {
		currentType = (Class) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];
		versionType = (Class) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[1];

	}
}

Now we have two generics types, T and E, representing respectively the Current and the Version DTO. We also have two Class variables, currentType and versionType.
Take notice that currentType is instantiated with the .getActualTypeArguments()[0], while versionType is instantiated with the .getActualTypeArguments()[1].

Then, we have to modify the update method so that when we call it a new Version will be stored and the modificated Current will be updated

public boolean update(T myDTO) throws IllegalArgumentException,
			SecurityException, java.lang.InstantiationException,
			IllegalAccessException, InvocationTargetException,
			NoSuchMethodException {
		boolean toReturn = false;
		try {
			AbsCurrentDTO previousDTO = 
                          (AbsCurrentDTO) findById(((AbsCurrentDTO) myDTO).getId());
			AbsVersionDTO version = 
                          (AbsVersionDTO) versionType.getConstructor(currentType).newInstance(previousDTO);
			version.setModificationDate(new Date());
			Session session = sessionFactory.openSession();
			Transaction tx = session.beginTransaction();
			session.save(version);
			session.update(myDTO);
			tx.commit();
			session.close();
			toReturn = true;
		} catch (HibernateException e) {
			e.printStackTrace();
		}
		return toReturn;
	}

The update method receive the current (just modified) object. We retrieve its saved instance (not yet modified) by its id, and then with this we create the version object. Then, we save the version (a new record will be created for each modification) and we update the current (only one record will be stored in the current table). I think a little bit of magic is in that line (:
AbsVersionDTO version =
(AbsVersionDTO) versionType.getConstructor(currentType).newInstance(previousDTO);
that allows us to create a new instance of an object whose type is known only at runtime, using a constructor that require as parameter another object whose type is also known only at runtime… where were we?

Last, we want to retrieve all the versions, of course:

public List listVersions(T myDTO) {
		List toReturn = null;
		try {
			Session session = sessionFactory.openSession();
			Transaction tx = session.beginTransaction();
			long currentId = ((AbsCurrentDTO) myDTO).getId();
			toReturn = session.createCriteria(versionType)
					.add(Restrictions.eq("currentId", currentId)).list();
			tx.commit();
			session.close();
		} catch (HibernateException e) {
			e.printStackTrace();
		}
		return toReturn;
	}

This is where the default constructor is needed by Hibernate . otherwise it can not instantiate the CustomerVersionDTO to put in the return list.

Here’s the complete class

public abstract class AbsDAO<T, E> {

	protected SessionFactory sessionFactory;

	private Class currentType;
	private Class versionType;

	public AbsDAO() {
		currentType = (Class) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];
		versionType = (Class) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[1];
		sessionFactory = new AnnotationConfiguration().configure()
				.buildSessionFactory();
	}
	public T findById(long id) {
		T toReturn = null;
		try {
			Session session = sessionFactory.openSession();
			Transaction tx = session.beginTransaction();
			toReturn = (T) session.get(currentType, id);
			tx.commit();
			session.close();
		} catch (NonUniqueResultException e) {
			e.printStackTrace();
		} catch (HibernateException e) {
			e.printStackTrace();
		}
		return toReturn;
	}

	public List list() {
		List toReturn = null;
		try {
			Session session = sessionFactory.openSession();
			Transaction tx = session.beginTransaction();
			toReturn = session.createCriteria(currentType).list();
			tx.commit();
			session.close();
		} catch (HibernateException e) {
			e.printStackTrace();
		}
		return toReturn;
	}

	public List listVersions(T myDTO) {
		List toReturn = null;
		try {
			Session session = sessionFactory.openSession();
			Transaction tx = session.beginTransaction();
			long currentId = ((AbsCurrentDTO) myDTO).getId();
			toReturn = session.createCriteria(versionType)
					.add(Restrictions.eq("currentId", currentId)).list();
			tx.commit();
			session.close();
		} catch (HibernateException e) {
			e.printStackTrace();
		}
		return toReturn;
	}

	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) {
			e.printStackTrace();
		}
		return toReturn;
	}

	public boolean update(T myDTO) throws IllegalArgumentException,
			SecurityException, java.lang.InstantiationException,
			IllegalAccessException, InvocationTargetException,
			NoSuchMethodException {
		boolean toReturn = false;
		try {
			AbsCurrentDTO previousDTO = (AbsCurrentDTO) findById(((AbsCurrentDTO) myDTO)
					.getId());
			AbsVersionDTO version = (AbsVersionDTO) versionType.getConstructor(
					currentType).newInstance(previousDTO);
			version.setModificationDate(new Date());
			Session session = sessionFactory.openSession();
			Transaction tx = session.beginTransaction();
			session.save(version);
			session.update(myDTO);
			tx.commit();
			session.close();
			toReturn = true;
		} catch (HibernateException e) {
			e.printStackTrace();
		}
		return toReturn;
	}

	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) {
			e.printStackTrace();
		}
		return toReturn;
	}
}

This is the class to test it all:

public class Main {

	/**
	 * (Insert comments here)
	 * 
	 * @param args
	 * @throws NoSuchMethodException
	 * @throws InvocationTargetException
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 * @throws SecurityException
	 * @throws IllegalArgumentException
	 */
	public static void main(String[] args) throws IllegalArgumentException,
			SecurityException, InstantiationException, IllegalAccessException,
			InvocationTargetException, NoSuchMethodException {
		VersionedDAO dao = new VersionedDAO();
		CustomerCurrentDTO current = new CustomerCurrentDTO();
		current.setMood("I am easy");
		current.setCreationDate(new Date());
		current.setName("FOO BAR");
		System.out.println(current.toString());
		dao.save(current);
		current.setMood("Now I've changed my mind");
		System.out.println(current.toString());
		dao.update(current);
		current.setMood("And now I'm tired");
		System.out.println(current.toString());
		dao.update(current);
		List versions = dao.listVersions(current);
		for (CustomerVersionDTO version : versions) {
			System.out.println(version);
		}
	}

This is the database structure (I called it versiondb):

mysql> show tables;
+---------------------+
| Tables_in_versiondb |
+---------------------+
| CUSTOMER_CURRENT    |
| CUSTOMER_VERSION    |
+---------------------+
2 rows in set (0.00 sec)

mysql> describe CUSTOMER_CURRENT;
+---------------+--------------+------+-----+---------+----------------+
| Field         | Type         | Null | Key | Default | Extra          |
+---------------+--------------+------+-----+---------+----------------+
| id            | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| CREATION_DATE | datetime     | NO   |     | NULL    |                |
| MOOD          | varchar(255) | YES  |     | NULL    |                |
| NAME          | varchar(255) | NO   |     | NULL    |                |
+---------------+--------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

mysql> describe CUSTOMER_VERSION;
+-------------------+--------------+------+-----+---------+----------------+
| Field             | Type         | Null | Key | Default | Extra          |
+-------------------+--------------+------+-----+---------+----------------+
| id                | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| currentId         | bigint(20)   | NO   |     | NULL    |                |
| MODIFICATION_DATE | datetime     | NO   |     | NULL    |                |
| MOOD              | varchar(255) | YES  |     | NULL    |                |
+-------------------+--------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

And here’s the sql to generate it (actually, I used Hibernate’s hbm2ddl.auto=create feature)

CREATE TABLE `CUSTOMER_CURRENT` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `CREATION_DATE` datetime NOT NULL,
  `MOOD` varchar(255) DEFAULT NULL,
  `NAME` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `id` (`id`)
);

CREATE TABLE `CUSTOMER_VERSION` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `currentId` bigint(20) NOT NULL,
  `MODIFICATION_DATE` datetime NOT NULL,
  `MOOD` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `id` (`id`)
);
Advertisements

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!