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.
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
You may also interested in:
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 { }
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:
- @EnableTransactionManagement – to enable Spring’s annotation-driven transaction management capability
- @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
Source Code
The complete source code is available at https://github.com/bytestree/spring4-hibernate5-example