Integrating Hibernate 5 with Spring 5 without JPA

Introduction

Java Persistence API (JPA) is a specification and Hibernate is one of the popular implementations of JPA. Hibernate provides some additional features that are not supported by JPA.

For example, Hibernate’s org.hibernate.query.Query interface provides a stream() method to retrieve a stream over the query results. This method uses ScrollableResultSet to scroll through the result set and fetch them in small batches instead of fetching all the records at once.

In order to make use of such Hibernate specific features, we are going to configure Hibernate 5 with Spring using MySQL database and create a repository to fetch stream of users using the stream() function.

Let’s Get Started

Add Dependencies

We are going to add spring-orm, hibernate-core and tomcat-dpcp dependencies in our existing spring application to integrate Hibernate.

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.javachinna</groupId>
	<artifactId>SpringHibernateDemo</artifactId>
	<packaging>jar</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>Spring 5 Hibernate 5 Integration</name>
	<url>http://maven.apache.org</url>
	<properties>
		<java-version>11</java-version>
		<spring.version>5.2.3.RELEASE</spring.version>
		<hibernate.version>5.4.1.Final</hibernate.version>
		<tomcat.version>9.0.37</tomcat.version>
		<slf4j.version>1.7.25</slf4j.version>
		<lombok.version>1.18.12</lombok.version>
		<junit.jupiter.version>5.7.0</junit.jupiter.version>
		<junit.platform.version>1.7.0</junit.platform.version>
	</properties>
	<dependencies>
		<!-- Spring Web MVC -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<!-- Servlet API -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>4.0.1</version>
			<scope>provided</scope>
		</dependency>
		<!-- Logback dependencies -->
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>1.2.3</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jul-to-slf4j</artifactId>
			<version>${slf4j.version}</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>log4j-over-slf4j</artifactId>
			<version>${slf4j.version}</version>
		</dependency>
		<!-- Spring ORM, hibernate libraries -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>${hibernate.version}</version>
		</dependency>
		<!-- Tomcat Database Connection Pooling package -->
		<dependency>
			<groupId>org.apache.tomcat</groupId>
			<artifactId>tomcat-dbcp</artifactId>
			<version>${tomcat.version}</version>
		</dependency>
		<!-- MySQL Connector -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>8.0.16</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>${lombok.version}</version>
		</dependency>
		<!-- Spring test and JUnit 5 libraries -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.junit.jupiter</groupId>
			<artifactId>junit-jupiter-engine</artifactId>
			<version>${junit.jupiter.version}</version>
		</dependency>
		<dependency>
			<groupId>org.junit.platform</groupId>
			<artifactId>junit-platform-runner</artifactId>
			<version>${junit.platform.version}</version>
		</dependency>
	</dependencies>
	<build>
		<finalName>SpringHibernate</finalName>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.3.2</version>
				<configuration>
					<source>${java-version}</source>
					<target>${java-version}</target>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-war-plugin</artifactId>
				<version>3.2.3</version>
				<configuration>
					<warSourceDirectory>src/main/webapp</warSourceDirectory>
					<warName>SpringHibernate</warName>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

Configure Hibernate

HibernateConf.java

This class is responsible for configuring DataSource, SessionFatory and HibernateTransactionManager

@EnableTransactionManagement enables Spring’s annotation-driven transaction management capability. It should be used on @Configuration classes

package com.javachinna.config;

import java.util.Properties;

import javax.sql.DataSource;

import org.apache.tomcat.dbcp.dbcp2.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement
public class HibernateConf {

	@Bean
	public LocalSessionFactoryBean sessionFactory() {
		LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
		sessionFactory.setDataSource(dataSource());
		sessionFactory.setPackagesToScan("com.javachinna.model");
		sessionFactory.setHibernateProperties(hibernateProperties());
		return sessionFactory;
	}

	@Bean
	public DataSource dataSource() {
		BasicDataSource dataSource = new BasicDataSource();
		dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
		dataSource.setUrl("jdbc:mysql://localhost:3306/demo?createDatabaseIfNotExist=true");
		dataSource.setUsername("root");
		dataSource.setPassword("secret");
		return dataSource;
	}

	@Bean
	public PlatformTransactionManager hibernateTransactionManager() {
		HibernateTransactionManager transactionManager = new HibernateTransactionManager();
		transactionManager.setSessionFactory(sessionFactory().getObject());
		return transactionManager;
	}

	private final Properties hibernateProperties() {
		Properties hibernateProperties = new Properties();
		// hibernateProperties.setProperty("hibernate.hbm2ddl.auto", "create-drop");
		hibernateProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");
		return hibernateProperties;
	}
}

Create Repository

Now we will be able to inject the raw Hibernate SessionFactory with @Autowired annotation in the Repository class as follows

package com.javachinna.repo;

import java.util.stream.Stream;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.javachinna.model.User;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Repository
@Transactional
public class UserRepository {

	@Autowired
	private SessionFactory sessionFactory;

	public Stream<User> getUers() {
		Session session = null;
		Transaction transaction = null;
		Stream<User> users = null;
		try {
			session = sessionFactory.openSession();
			transaction = session.beginTransaction();
			users = session.createQuery("select u from User u", User.class).stream();
			transaction.commit();
		} catch (Exception e) {
			log.error("Exception occurred", e);
			if (transaction != null) {
				transaction.rollback();
			}
		} finally {
			session.close();
		}
		return users;
	}
}

After processing the Stream<Users> returned by the UserRepository, we should call Stream#close() method to deallocate the underlying resource right away.

			users.map(u -> u.getDisplayName()).forEach(n -> log.info(n));
			users.close();

Source Code

https://github.com/JavaChinna/spring-hibernate-integration

References

https://www.baeldung.com/hibernate-5-spring

Conclusion

That’s all folks. In this article, we have bootstrapped Hibernate 5 with Spring without JPA.

Please share the article with your friends if you like it. Thank you for reading.

Read Next: Call Stored Procedure and Map the Native Query Result to POJO with Hibernate

Leave a Reply