In my current work, I am responsible for developing a platform to ease web application development using several JEE technologies such as JSF, SWF, Spring, and JPA/Hibernate or Toplink. One main consideration of our management is to keep platform as flexible as possible in terms of which ORM tool is used. Every web application that is built on top this platform should be able to choose either Toplink or Hibernate.
I am aware of impossibility of keeping a platform ORM agnostic with the use of JPA. My aim in this post is not to criticize JPA, but share a simple way of isolating ORM vendor specific codes in a layered arhitecture. We employ classical layered approach in our platform, in which presentation, web controller, service and data access layers exist. In service layer, we provide several general purpose services, such as transactional CRUD operation service, security, and auditing services etc. We usually perform data access in those services, and therefore, a DAO layer with related data access operations are laid underneath.
During development process we came up with such situations that it is necessary to provide both Hibernate and Toplink specific implementations of those DAO classes. One such example exist in generic CRUD operations. We want to provide a QBE facility through our services layer, however, each ORM vendor have their proprietary solutions of it. As a result, following class hierarchy appeared in our entityDao facility.
It is highly possible to have similar hierarchies for other DAO classes as well. In runtime we want to isolate higher layers, such as service layer, from ORM vendor choice, and make the system functioning with either Hibernate or Toplink by just some configuration property changes. In other words, we want to provide our services layer with a family of related objects, in this case DAO objects, depending on a system configuration. As you remember from seminal book of GOF, abstract factory makes changing product families very easy and also promotes consistency among products, that is it is not possible to mix products from different families. On the other hand, it is difficult to introduce new products into the scene, as it requires changing abstract factory interface. Unfortunately, we cannot foresee what kind of DAO classes we will need, hence it is critical for us to be able to add new kinds of DAO’s without any effort or modification in the system.
You may ask, how Spring help us in this situation. Well, it is its bean configuration and overriding mechanism that help us to solve adding new kinds of products in the product families. Spring bean configuration mechanism has many nice and useful features, and one of them I often make use of it is its bean overriding mechanism. You can define more than one bean with same id in your application context files, and the one in the last loaded application context file will override other bean definitions with same ids. As a result, we only need to create different bean configuration files for each different ORM vendor and define exactly same beans with ids in each of them. For example, in our services.xml. we have;
<bean id="entityService" class="org.ems4j.services.EntityServiceImpl"> <property name="entityDao" ref="entityDao"/> </bean> <bean id="securityService" class="org.ems4j.services.SecurityServiceImpl"> <property name="entityDao" ref="entityDao"/> <property name="securityDao" ref="securityDao"/> <property name="encodedPasswordCreator" ref="encodedPasswordCreator"/> </bean>
As you see our entityService and securityService beans depend on entityDao and securityDao beans. All we need to create daos-hibernate.xml and daos-toplink.xml like below, and configure only one of them in the runtime.
<bean id="entityDao" class="org.ems4j.dao.EntityDaoJpaHibernateImpl"> <property name="jpaTemplate" ref="jpaTemplate"/> </bean> <bean id="securityDao" class="org.ems4j.dao.SecurtityDaoJpaHibernateImpl"> <property name="jpaTemplate" ref="jpaTemplate"/> </bean>
<bean id="entityDao" class="org.ems4j.dao.EntityDaoJpaToplinkImpl"> <property name="jpaTemplate" ref="jpaTemplate"/> </bean> <bean id="securityDao" class="org.ems4j.dao.SecurityDaoJpaToplinkImpl"> <property name="jpaTemplate" ref="jpaTemplate"/> </bean>
When loading spring application context, you may list those bean configuration files in your web.xml as follows. Let’s assume you chose Hibernate as ORM in this case;
<context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath*:/appcontext/services.xml classpath*:/appcontext/daos-hibernate.xml </param-value> </context-param>
In summary, when you need to add new DAO classes in your platform, all you need to implement them in your code base and add their bean definitions into corresponding bean configuration files.