package com.dyna.frm.commons.dao;
import java.io.Serializable;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
public interface BaseDAO<T, PK extends Serializable> {
public void createOrUpdate(T... entity);
@Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
public T read(PK id);
public void update(T id);
public void delete(PK... id);
}
Here i have created four methods to deal with the main four CRUD operations that anyone will have to deal with. I have specified the transactions at the interface layer and have opted to go for annotation based transactions due to rarity of transactions being changed and i didnt want to clutter by XML configuration with it. At the top most layer i have defined as transaction equired and read only false. This is because only the read method does not need a transaction and whilst others do. As you know REQUIRED will create a new transaction if one is not already provided.
Note that i have used NOT_SUPPORTED for the read method as retrievals do not need to run within a transaction and hence it is just over head to create a transaction for retrievals.
Moving on next i show you the BaseDAO implementation class;
package com.dyna.frm.commons.dao.daoimpl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import com.dyna.frm.commons.dao.BaseDAO;
public class BaseDAOHiberanateImpl<T, PK extends java.io.Serializable> extends
HibernateDaoSupport implements BaseDAO<T, PK> {
private Class<T> entityClass;
public BaseDAOHiberanateImpl(Class<T> entityClass) {
this.entityClass = entityClass;
}
@Override
public void createOrUpdate(T... entity) {
getHibernateTemplate().saveOrUpdateAll(Arrays.asList(entity));
}
@SuppressWarnings("unchecked")
@Override
public T read(PK id) {
return (T) getHibernateTemplate().get(entityClass, id);
}
@Override
public void update(T entity) {
getHibernateTemplate().update(entity);
}
@Override
public void delete(PK... id) {
if (id.length == 1) {
T entity = read(id[0]);
getHibernateTemplate().delete(entity);
} else {
List<T> entityList = new ArrayList<T>();
for (PK pk : id) {
T entity = read(pk);
entityList.add(entity);
}
getHibernateTemplate().deleteAll(entityList);
}
}
}
As you can see here i have extended from Spring's HibernateDAOSupport class. The good thing about using the HibernateTemplate is that it encapsulates all those nitty gritty database exceptions and wraps it around Spring's DAO exception stack. The good thing is Spring's Exceptions are runtime and hence are not forced to catch any exceptions if you do not need to.
The best approach of handling exceptions is i believe through writing an Exception Interceptor to intercept all your DAO calls and you can catch the ones you only need in your application and wrap it with your own custom application specific exceptions.
Anyhow moving on with the implementation class for create or update i have used the saveOrUpdate method of Hibernate template and as i have used var args in the method parameters i can process both single entity persistence as well as batch entity persistence within one method.All the other methods are self explanatory as i see it. For delete also as i have provided the ability for the user to send a collection of PK values to delete from i have again used two different methods from the Hibernate template checking whether the varg args length is greater than zero.
Next i show you a sample usage of this base dao implementation i have provided by creating a DAO for a hypothetical Person entity;
package com.dyna.frm.controller.dao;
import com.dyna.frm.commons.dao.BaseDAO;
import com.dyna.frm.controller.domain.Person;
public interface PersonDAO extends BaseDAO<Person, Long>{
}
package com.dyna.frm.controller.dao.hibernate;
import org.springframework.beans.factory.annotation.Qualifier;
import com.dyna.frm.commons.dao.daoimpl.BaseDAOHiberanateImpl;
import com.dyna.frm.controller.dao.PersonDAO;
import com.dyna.frm.controller.domain.Person;
@Qualifier("personDAOHibernate")
public class PersonDAOImpl extends BaseDAOHiberanateImpl<Person, Long> implements PersonDAO{
public PersonDAOImpl() {
super(Person.class);
}
}
I have defined a qualifier here because if i ever needed to use JpaTemplate then i wouldnt have conflicts when i try to Auto wire based on the PersonDAO interface.As you can see within the constructor i have called the BaseDAOImpl class constructor passing in the entity we need to manage within this DAO class. And all other basic methods are now available to this PersonDAO and you can add any additional methods you need as you progress.
And finally i give you the XML configuration needed to wire this up;
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<tx:annotation-driven />
<!-- JTA Transaction Mgr is used for JEE applications -->
<bean id="transactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="autodetectTransactionManager" value="true" />
</bean>
<bean id="personDAO" class="com.dyna.frm.controller.dao.hibernate.PersonDAOImpl">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan">
<list>
<value>com.dyna.frm.**.*</value>
</list>
</property>
<property name="hibernateProperties">
<ref bean="defaultHibernateProps" />
</property>
</bean>
<!-- Hibernate properties bean -->
<bean id="defaultHibernateProps"
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="hibernate.dialect">${dyna.frm.db.hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${dyna.frm.db.show.sql}</prop>
</props>
</property>
</bean>
</beans>
Note that here i have used JTA transactions and as i am running this within jboss i have used the autodetectTransactionManager property.
One notable thing here is that within the sessionFactory bean definition i have used the attribute packagesToScan. Why i have defined this way is because now i do not need to define each annotated class individually as Spring will scan all sub packages within com.dyna.frm so that all classes that are annotated with @Entity will be enclosed. I have used PropertyPlaceHolder to get the dialect names and other parameters.
Thats it. If you have any queries do drop a comment.
See the single sentence in http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/classic-spring.html#classic-spring-hibernate paragraph. Spring recommends now direct Hibernate (or JPA) API instead of HibernateTemplate.
ReplyDeleteregards
Grzegorz Grzybek
Hi Grzegorz,
ReplyDeleteYes you do have a point here. But the thing is then you will have to manage all the hibernate specific exceptions isnt it? that means you will be bound to a particular provider? what are your thoughts on this?
Regards,
Dinuka
Very interresting. A nice to way to hanlde the crud for every sub clas that will extends the BaseDAOHiberanateImpl.
ReplyDeletethx alot for your feed back.. appreciate it.
ReplyDelete