Hibernate GenericDao

Need for GenericDao

When we use hibernate in a typical J2EE project, there are some basic operations which must be supported on all hibernate entities. These operations are create, update, delete, find, findAll entities.  A generic code can be written which performs these basic operations for the all entities. This approach has following advantages

  • Generic code allows in removing the code duplication. It will save lot of time spent in writing/testing duplicate code.
  • It ensures the consistent interface. Consistent interface helps developer understand and implement code much faster

Sometimes the second point consistent interface does not seem too much of value, but I have seen projects where the interface was not consistent and due to which developer spent hours and hours of time just to understand the code and implement the things which were redundant.

 

How to write GenericDao

GenericDao needs to be written using Generics, so that it can support any hibernate entity. There is an interface GenericDao and it’s implementation GenericDaoImpl. All the Dao interface should extend the GenericDao interface and all the dao implementation should extend the GenerficDaoImpl implementation.

package in.pm.hibernate.genericdao_example.dao;

import java.io.Serializable;
import java.util.List;

public interface GenericDao<T, PK extends Serializable> {

    public PK create(T object);
    
    public T find(PK primaryKey);
    
    public List<T> finalAll();
    
    public void update(T object);
    
    public void delete(T object);
}

Following is the implementation for GenericDao. Here hibernate is configured directly with Spring and no JPA is used. But if we want to use the JPA then also the approach remains the same. We get the actual type of class by looking at the type argument of the generic class.

package in.pm.hibernate.genericdao_example.dao.impl;

import in.pm.hibernate.genericdao_example.dao.GenericDao;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

public class GenericDaoImpl<T, PK extends Serializable> implements
        GenericDao<T, PK> {

    private Class<T> actualType;

    @Autowired
    private SessionFactory sessionFactory;

    @SuppressWarnings("unchecked")
    public GenericDaoImpl() {

        // get superclass declaration
        ParameterizedType genericSuperClass = (ParameterizedType) getClass()
                .getGenericSuperclass();
        // Find out the actual type
        this.actualType = (Class<T>) genericSuperClass.getActualTypeArguments()[0];

    }

    public Session getSession() {
        return sessionFactory.getCurrentSession();
    }

    @SuppressWarnings("unchecked")
    @Transactional
    public PK create(T object) {
        PK id = (PK) getSession().save(object);
        return id;
    }

    public T find(PK primaryKey) {
        T object = getSession().get(actualType, primaryKey);
        return object;
    }

    @SuppressWarnings("unchecked")
    public List<T> finalAll() {
        String findAllQueryStr = "from " + actualType.getName();
        Query findAllQuery = getSession().createQuery(findAllQueryStr);
        List<T> objects = (List<T>) findAllQuery.list();
        return objects;
    }

    public void update(T object) {
        getSession().update(object);
    }

    public void delete(T object) {
        getSession().delete(object);
    }

}

 

Usage of GenericDao

We will take two entities (Device, User) and try to see how GenericDao is useful in doing db operations for these two entities.

package in.pm.hibernate.genericdao_example.entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

import org.hibernate.annotations.GenericGenerator;

@Entity
@Table(name = "Device")
public class Device {

    @Id
    @GeneratedValue(generator="increment")
    @GenericGenerator(name= "increment", strategy="increment")
    private Long id;
    
    private String deviceName;
    
    private String deviceType;

    
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getDeviceName() {
        return deviceName;
    }

    public void setDeviceName(String deviceName) {
        this.deviceName = deviceName;
    }

    public String getDeviceType() {
        return deviceType;
    }

    public void setDeviceType(String deviceType) {
        this.deviceType = deviceType;
    }
    
     
}

 

package in.pm.hibernate.genericdao_example.entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

import org.hibernate.annotations.GenericGenerator;

@Entity
@Table(name = "USER")
public class User {

    @Id
    @GeneratedValue(generator = "increment")
    @GenericGenerator(name = "increment", strategy = "increment")
    private Long id;

    private String firstName;
    private String lastName;
    private String email;
    
    
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    
    
}

 

Dao for Entity

For each entity Dao class needs to be created which will be responsible for doing basic CRUD operation on entity and will support additional query if required.

Dao Interfaces

package in.pm.hibernate.genericdao_example.dao;

import in.pm.hibernate.genericdao_example.entity.Device;

public interface DeviceDao extends GenericDao<Device, Long>{

}
package in.pm.hibernate.genericdao_example.dao;

import in.pm.hibernate.genericdao_example.entity.User;

public interface UserDao extends GenericDao<User, Long> {
    
    public User getUserByName(String firstName);
}

 

Dao Implementation

package in.pm.hibernate.genericdao_example.dao.impl;

import org.springframework.stereotype.Repository;

import in.pm.hibernate.genericdao_example.dao.DeviceDao;
import in.pm.hibernate.genericdao_example.entity.Device;

@Repository
public class DeviceDaoImpl extends GenericDaoImpl<Device, Long> implements DeviceDao{

}
package in.pm.hibernate.genericdao_example.dao.impl;

import org.hibernate.Query;
import org.springframework.stereotype.Repository;

import in.pm.hibernate.genericdao_example.dao.UserDao;
import in.pm.hibernate.genericdao_example.entity.User;

@Repository
public class UserDaoImpl extends GenericDaoImpl<User, Long> implements UserDao{

    public User getUserByName(String firstName) {
        String userByName = "from User where user.firstName = :firstName ";
        Query userByNameQuery = getSession().createQuery(userByName);
        userByNameQuery.setString("firstName", firstName);
        return (User)userByNameQuery.uniqueResult();
    }
    
}

 

As we can see that since we have used generic dao, methods (create, update, delete, find and findAll)  are automatically inherited by both dao (DeviceDao and UserDao).  UserDao had additional requirement for finding user according to its name, so only that method is implemented in the UserDao.

This has ensured code is reused and consistent interface. For e.g. if we want to store any entity, we just have to call create method of that entity’s dao. Similar is the case for update, delete, find and findAll.

 

Test Application

Below is the test class which was used for testing. It creates two entity (User and Device) and inserts it into database.

package in.pm.hibernate.genericdao_example.app;

import in.pm.hibernate.genericdao_example.dao.DeviceDao;
import in.pm.hibernate.genericdao_example.dao.UserDao;
import in.pm.hibernate.genericdao_example.entity.Device;
import in.pm.hibernate.genericdao_example.entity.User;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class App {
    
    @SuppressWarnings("resource")
    public static void main(String[] args) {
        
        ApplicationContext context = 
                new AnnotationConfigApplicationContext(AppConfiguration.class);
        
        UserDao userDao = context.getBean(UserDao.class);
        DeviceDao deviceDao = context.getBean(DeviceDao.class);
        
        User user = new User();
        user.setFirstName("Pranav");
        user.setLastName("Maniar");
        user.setEmail("pranav9428@gmail.com");
        
        Device device = new Device();
        device.setDeviceName("Moto G");
        device.setDeviceType("Mobile");
        
        userDao.create(user);
        deviceDao.create(device);
        
    }

}

 

Configure Hibernate5 with Spring4 using java configuration

To configure Hibernate5 with Spring4 using java configuration Spring Configuration class needs to be created. Also one property file containing information about database username/password , connection string, hibernate settings, etc will be required.

Below is the property file which is used during the configuration.

## jdbc configuration 
driverclass = com.mysql.jdbc.Driver
jdbcurl = jdbc:mysql://localhost/db
username = test
password = test

## hibernate configuration
hibernate.dialect = org.hibernate.dialect.MySQLDialect
hibernate.show_sql = true
hibernate.hbm2ddl = create

 

Configuration Class

@Configuration
@ComponentScan(basePackages = { "in.pm.hibernate.genericdao_example" })
@EnableTransactionManagement
public class AppConfiguration {

}

First create a configuration class and annotate it with following annotations

  • @Configuration : Through this annotation Spring will know that this is the java config class
  • @ComponentScan : Specifies the list of base packages which contains Spring beans and hibernate entities. When the spring container starts it will scan these packages and register the beans by reading annotations
  • @EnableTranscationManagement : This will enable spring’s annotation driven transaction management capability

 

 
    @Value("${driverclass}") 
    private String driverClass;
    
    @Value("${jdbcurl}")
    private String jdbcURL;
    
    @Value("${username}")
    private String userName;
    
    @Value("${password}")
    private String password;
    
    @Value("${hibernate.dialect}")
    private String hibernateDialect;
    
    @Value("${hibernate.show_sql}")
    private String hibernateShowSql;
    
    @Value("${hibernate.hbm2ddl}")
    private String hibernateHbm2ddlAuto;

Add properties and annotate it with appropriate value expression, so that value will be taken from property file and bound to the property

 

    @Bean
    public PropertyPlaceholderConfigurer getPropertyPlaceHolderConfigurer() {
        PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
        ppc.setLocation(new ClassPathResource("application.properties"));
        ppc.setIgnoreUnresolvablePlaceholders(true);
        return ppc;
    }

Create PropertyPlaceHolderConfigurer and provide it with the location of the property file. It will read the properties and populate fields defined in above step

 

    @Bean
    public DataSource getDataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(driverClass);
        dataSource.setUrl(jdbcURL);
        dataSource.setUsername(userName);
        dataSource.setPassword(password);
        return dataSource;
    }

    public Properties getHibernateProperties() {
        Properties properties = new Properties();
        properties.put("hibernate.dialect", hibernateDialect);
        properties.put("hibernate.show_sql", hibernateShowSql);
        properties.put("hibernate.hbm2ddl.auto", hibernateHbm2ddlAuto);
        return properties;
    }

    //Create a LocalSessionFactoryBean which will be used to create hibernate SessionFactory
    @Bean
    @Autowired
    public LocalSessionFactoryBean getSessionFactory(DataSource dataSource) {
        LocalSessionFactoryBean sfb = new LocalSessionFactoryBean();
        sfb.setDataSource(dataSource);
        sfb.setPackagesToScan("in.pm.hibernate.genericdao_example.entity");
        sfb.setHibernateProperties(getHibernateProperties());
        return sfb;
    }

Now, create datasource and inject this datasource into the function which is used to create SessionFactory.

Create LocalSessionFactoryBean and set the datasource and hibernate properties. Also set the packages to be scanned for hibernate entities. LocalSessionFactoryBean is a Spring FactoryBean which is used to create Hibernate SessionFactory.

 

    @Bean
    @Autowired
    public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
        HibernateTransactionManager tm = new HibernateTransactionManager();
        tm.setSessionFactory(sessionFactory);
        return tm;
    }

Finally create HibernateTransactionManager and assigned it the sessionFactory which was created in the previous step.

NOTE: Please use LocalSessionFactoryBean and HibernateTransactionManager which is under package “org.springframework.orm.hibernate5”.

 

Complete configuration class looks as below :

package in.pm.hibernate.genericdao_example.app;

import java.util.Properties;

import javax.sql.DataSource;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@ComponentScan(basePackages = { "in.pm.hibernate.genericdao_example" })
@EnableTransactionManagement
public class AppConfiguration {

    
    @Value("${driverclass}") 
    private String driverClass;
    
    @Value("${jdbcurl}")
    private String jdbcURL;
    
    @Value("${username}")
    private String userName;
    
    @Value("${password}")
    private String password;
    
    @Value("${hibernate.dialect}")
    private String hibernateDialect;
    
    @Value("${hibernate.show_sql}")
    private String hibernateShowSql;
    
    @Value("${hibernate.hbm2ddl}")
    private String hibernateHbm2ddlAuto;
    
    @Bean
    public PropertyPlaceholderConfigurer getPropertyPlaceHolderConfigurer() {
        PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
        ppc.setLocation(new ClassPathResource("application.properties"));
        ppc.setIgnoreUnresolvablePlaceholders(true);
        return ppc;
    }

    @Bean
    public DataSource getDataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(driverClass);
        dataSource.setUrl(jdbcURL);
        dataSource.setUsername(userName);
        dataSource.setPassword(password);
        return dataSource;
    }

    public Properties getHibernateProperties() {
        Properties properties = new Properties();
        properties.put("hibernate.dialect", hibernateDialect);
        properties.put("hibernate.show_sql", hibernateShowSql);
        properties.put("hibernate.hbm2ddl.auto", hibernateHbm2ddlAuto);
        return properties;
    }

    //Create a LocalSessionFactoryBean which will be used to create hibernate SessionFactory
    @Bean
    @Autowired
    public LocalSessionFactoryBean getSessionFactory(DataSource dataSource) {
        LocalSessionFactoryBean sfb = new LocalSessionFactoryBean();
        sfb.setDataSource(dataSource);
        sfb.setPackagesToScan("in.pm.hibernate.genericdao_example.entity");
        sfb.setHibernateProperties(getHibernateProperties());
        return sfb;
    }

    //NOTE: Use HibernateTransactionManager which is under hibernate5 package only.
    @Bean
    @Autowired
    public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
        HibernateTransactionManager tm = new HibernateTransactionManager();
        tm.setSessionFactory(sessionFactory);
        return tm;
    }

}