Spring 4 + Hibernate 5 Example


This article is an example of java application showing the integration of  Spring 4 and hibernate 5. It explains how to configure Hibernate 5 and Spring 4 along with transaction manager to perform database operations. All configurations are done in java classes using annotations.

Advertisements

Our application has following features:

  • Standalone Spring application performing database operations
  • Uses Hibernate transaction manager
  • In-memory database (HSQLDB)
  • Multi-layered application with Service and DAO layer

Technology Stack

  • Spring Framework – 4.3.4.RELEASE
  • Hibernate – 5.2.5.Final
  • Database – HSQLDB
  • JDK 8

 

Project Dependencies

Let’s start with the project dependencies first. As per technology stack, we require following dependencies in our pom.xml

<properties>
	<maven.compiler.source>1.8</maven.compiler.source>
	<maven.compiler.target>1.8</maven.compiler.target>
	<spring.version>4.3.4.RELEASE</spring.version>
	<hibernate.version>5.2.5.Final</hibernate.version>
</properties>

<dependencies>
	<!-- Hiberante -->
	<dependency>
		<groupId>org.hibernate</groupId>
		<artifactId>hibernate-core</artifactId>
		<version>${hibernate.version}</version>
	</dependency>
	
	<!-- Spring -->
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-orm</artifactId>
		<version>${spring.version}</version>
	</dependency>
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-context</artifactId>
		<version>${spring.version}</version>
	</dependency>

	<!-- Database -->
	<dependency>
		<groupId>org.hsqldb</groupId>
		<artifactId>hsqldb</artifactId>
		<version>2.3.4</version>
	</dependency>
	
	<!-- Logging -->
	<dependency>
		<groupId>log4j</groupId>
		<artifactId>log4j</artifactId>
		<version>1.2.17</version>
	</dependency>

</dependencies>

 

Bootstrap Application

For bootstrapping the application we have one @Configuration class which configures component scanning using @ComponentScan annotation. This class will be used to initialize spring’s application context and test the application.

package com.bytestree.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(value={"com.bytestree"})
public class AppConfiguration {
	
}

 

Advertisements

Hibernate 5 Configuration

This is the main configuration of this article and will be done in a separate @Configuration class HibernateConfig.java. We are configuring following things in this class:

  • DataSource
  • SessionFactory
  • Transaction Manager

Before configuring above things, take a look at some class level configurations.

This class has two more annotations:

  1. @EnableTransactionManagement – to enable Spring’s annotation-driven transaction management capability
  2. @PropertySource – Add PropertySouce to Spring’s Environment from .properties file
@Configuration
@EnableTransactionManagement
@PropertySource(value = { "classpath:application.properties" })
public class HibernateConfig {

	@Autowired
	private Environment env;

        //Beans
}

 

And the properties file which contains all configurable properties is:

# datasource 
datasource.driver=org.hsqldb.jdbcDriver
datasource.url=jdbc:hsqldb:mem:testdb
datasource.username=sa
datasource.password=

#Hibernate
hibernate.dialect=org.hibernate.dialect.HSQLDialect
hibernate.hbm2ddl.auto=create-drop
hibernate.show_sql=true
hibernate.batch.size=20
hibernate.current.session.context.class=org.springframework.orm.hibernate5.SpringSessionContext

 

Now take a look at each configuration/Bean define in this class:

DataSource

Create DataSource Bean from DriverManagerDataSource class. You need to provide driver name, database URL, username and password defined in our application.properties file.

@Bean
public DataSource getDataSource() {
	DriverManagerDataSource dataSource = new DriverManagerDataSource();
	dataSource.setDriverClassName(env.getRequiredProperty("datasource.driver"));
	dataSource.setUrl(env.getRequiredProperty("datasource.url"));
	dataSource.setUsername(env.getRequiredProperty("datasource.username"));
	dataSource.setPassword(env.getRequiredProperty("datasource.password"));
	return dataSource;
}

 

SessionFactory

We are using LocalSessionFactoryBean class provided by Spring to define Hibernate SessionFactory. Make sure this class is imported from org.springframework.orm.hibernate5 package. You need to set dataSource, packages to scan for model classes and various hibernate configuration properties to the instance of this class.

@Bean
public LocalSessionFactoryBean getSessionFactory() {
	LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
	sessionFactory.setDataSource(getDataSource());
	sessionFactory.setPackagesToScan(new String[] { "com.bytestree.model" });
	sessionFactory.setHibernateProperties(getHibernateProperties());
	return sessionFactory;
}

private Properties getHibernateProperties() {
	Properties properties = new Properties();
	properties.put(AvailableSettings.DIALECT, env.getRequiredProperty("hibernate.dialect"));
	properties.put(AvailableSettings.SHOW_SQL, env.getRequiredProperty("hibernate.show_sql"));
	properties.put(AvailableSettings.STATEMENT_BATCH_SIZE, env.getRequiredProperty("hibernate.batch.size"));
	properties.put(AvailableSettings.HBM2DDL_AUTO, env.getRequiredProperty("hibernate.hbm2ddl.auto"));
	properties.put(AvailableSettings.CURRENT_SESSION_CONTEXT_CLASS, env.getRequiredProperty("hibernate.current.session.context.class"));
	return properties;
}

 

Transaction Manager

The last configuration is Transaction manager. We use HibernateTransactionManager provided by Spring framework. Here too, make sure this class is imported from org.springframework.orm.hibernate5 package. You need to set the SessionFactory to create earlier to this transaction manager instance.

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

 

DAO Layer

Our DAO class is annotated with @Repository. We are going add just two database operations in this class. First one to store an employee in database and second to retrieve the saved employee from the database. The SessionFactory is autowired in our DAO class. Just get the current session from SessionFactory and call save and get methods to perform respective database operations.

@Repository
public class EmployeeDaoImpl implements EmployeeDao {
	
	@Autowired
	private SessionFactory sessionFactory;

	protected Session getSession() {
		return this.sessionFactory.getCurrentSession();
	}
	
	@Override
	public Serializable save(Employee employee) {
		return getSession().save(employee);
	}
	
	@Override
	public Employee findById(final Serializable id) {
		return getSession().get(Employee.class, id);
	}
}

Service Layer

The service class is annotated with @Service and executes the respective DAO layer method from autowired DAO instance. The main thing to note in this class is @Transactional annotation which defines the transaction attribute for a class or method. At the class level, we configure it as readOnly to make all transactions in this class as read-only. This is general practice to void accidental write operation when a method should only read the data. We need to override this setting for methods that should perform write operations. For example, addNewEmployee method in this class.

@Service
@Transactional(readOnly = true)
public class EmployeeServiceImpl implements EmployeeService {

	final static Logger logger = Logger.getLogger(EmployeeServiceImpl.class);

	@Autowired
	EmployeeDao employeeDao;

	public Employee getEmployee(Long id) {
		logger.debug("Getting employee with id " + id);
		return employeeDao.findById(id);
	}

	@Override
	@Transactional(readOnly = false)
	public void addNewEmployee(Employee employee) {
		Long id = (Long) employeeDao.save(employee);
		logger.debug("Id of new Employee " + id);
	}
}

Main Application

After we have all configurations and application layers set properly, let’s start writing the main application class which makes a use of all these things. Our application is nothing but a Component class which calls service layer methods. First, it saves an employee and then retrieve the same from database in a single method performDbTasks().

@Component
public class MyApplication {

	final static Logger logger = Logger.getLogger(MyApplication.class);
	
	@Autowired
	private EmployeeService empService;

	public void performDbTasks()
	{
		Employee empNew = new Employee(1l, "Bytes", "Tree", "Senior Developer", 2000);

		// Save new employee
		empService.addNewEmployee(empNew);

		// Get saved employee
		Employee employee = empService.getEmployee(empNew.getId());
		logger.debug("Retrieving saved employee " + employee);
	}
}

 

Finally, we need a test class which will initialize the Spring’s application context and execute our main application.

public class TestApplication {

	public static void main(String[] args) {

		AnnotationConfigApplicationContext context = null;

		try {
			context = new AnnotationConfigApplicationContext(AppConfiguration.class);
			MyApplication application = context.getBean(MyApplication.class);
			application.performDbTasks();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			context.close();
		}	    
	}
}

 

If everything is correct you will see a log like this after executing the TestApplication.java

INFO  AnnotationConfigApplicationContext:582 - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4dcbadb4: startup date [Mon Dec 26 00:54:49 CET 2016]; root of context hierarchy
INFO  AutowiredAnnotationBeanPostProcessor:155 - JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
INFO  DriverManagerDataSource:133 - Loaded JDBC driver: org.hsqldb.jdbcDriver
INFO  Version:45 - HHH000412: Hibernate Core {5.2.5.Final}
INFO  Environment:213 - HHH000206: hibernate.properties not found
INFO  Version:66 - HCANN000001: Hibernate Commons Annotations {5.0.1.Final}
INFO  Dialect:157 - HHH000400: Using dialect: org.hibernate.dialect.HSQLDialect
Hibernate: drop table employee if exists
Hibernate: create table employee (id bigint generated by default as identity (start with 1), designation varchar(50), firstname varchar(50), lastname varchar(50), salary integer, primary key (id))
INFO  SchemaCreatorImpl:488 - HHH000476: Executing import script 'org.hibernate.tool.schema.internal.exec.ScriptSourceInputNonExistentImpl@6134ac4a'
INFO  HibernateTransactionManager:357 - Using DataSource [org.springframework.jdbc.datasource.DriverManagerDataSource@57a3af25] of Hibernate SessionFactory for HibernateTransactionManager
Hibernate: insert into employee (id, designation, firstname, lastname, salary) values (default, ?, ?, ?, ?)
DEBUG EmployeeServiceImpl:41 - Id of new Employee 1
DEBUG EmployeeServiceImpl:27 - Getting employee with id 1
Hibernate: select employee0_.id as id1_0_0_, employee0_.designation as designat2_0_0_, employee0_.firstname as firstnam3_0_0_, employee0_.lastname as lastname4_0_0_, employee0_.salary as salary5_0_0_ from employee employee0_ where employee0_.id=?
DEBUG MyApplication:33 - Retrieving saved employee Id: 1, firstName: Bytes, lastName: Tree, Designation: Senior Developer, Salary: 2000
INFO  AnnotationConfigApplicationContext:987 - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@4dcbadb4: startup date [Mon Dec 26 00:54:49 CET 2016]; root of context hierarchy
INFO  SchemaDropperImpl$DelayedDropActionImpl:523 - HHH000477: Starting delayed drop of schema as part of SessionFactory shut-down'
Hibernate: drop table employee if exists

 

Advertisements

Source Code

The complete source code is available at https://github.com/bytestree/spring4-hibernate5-example