Getting Intimate with Spring Boot and Hibernate

In my previous blog , we looked at how to get started with a simple Spring Boot and Hibernate application. We managed to get our application up and running in a few minutes.

In this article, we will look at how and what Spring Boot does behind the scenes.

Basic requirement

To setup a Spring based application with Hibernate/JPA we usually need the following:

  1. Datasource  which connects to a database and details like the database url, password, username – this is really independent of whether we use Spring, JPA.
  2. JPA EntityManager to perform repository(CRUD) related operations.
  3. Vendor specific (Hibernate) properties
  4. Transaction support.

Let us take a look at how these things get configured.

In case of an in memory H2 database, all of the above was configured automatically by Spring Boot. We did not write a single configuration either using Java configuration or XML. Well, how does Spring Boot do everything for us?  Let’s take a look :

Vanishing (configuration) act explained :

1. The starting point is the spring.factories file. This file has a Auto Configure section which Spring Boot uses to determine what should be auto configured. This file is in the META-INF folder which is part of the spring-boot-autoconfigure-<version>.RELEASE.jar. The SpringFactoriesLoader class loads the spring.factories file.

2. Since we are talking about JPA/Hibernate, the spring.factories contains a (among other autoconfigurations) key value pair: org.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration

3. The key EnableAutoConfiguration above is triggered using the @SpringBootApplication annotation on the SpringBootJpaApplication.java file. This is our starting point in our application.


package com.boot.jpa;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootJpaApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootJpaApplication.class, args);
}
}

4. The value part above, HibernatJpaAutoConfiguration class which is part of the Spring framework looks like


@Configuration
@ConditionalOnClass({ LocalContainerEntityManagerFactoryBean.class, EntityManager.class })
@Conditional(HibernateEntityManagerCondition.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class })
public class HibernateJpaAutoConfiguration extends JpaBaseConfiguration {
….
}

One of the important annotations above is on line 4 :

@AutoConfigureAfter({ DataSourceAutoConfiguration.class }).

This indicates that we should perform HibernateJpaAutoConfiguration only after reading the DataSourceAutoConfiguration.class or only if a Datasource is found on the classpath. It would make no sense to configure JPA and Hibernate without a datasource/database !

5. The DataSourceAutoConfiguration looks as follows:


@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class })
public class DataSourceAutoConfiguration {
}

6. This is a normal Spring Configuration class which sets up the DataSource only if all of the above annotations satisfy our criteria. We used an H2 database and hence the DataSource.class and EmbeddedDatabaseType.class satisfies the condition.

7. Now the configuration moves back to step 4 where we needed the presence of  datasource/database at minimum. Now on to the next part where the following is checked :

@ConditionalOnClass({ LocalContainerEntityManagerFactoryBean.class, EntityManager.class })

This is configuration specific to JPA classes and the @ConditionalOnClass checks if these classes are there on the class path. There are present in our case through the Spring Data JPA library.

8.Next comes the @Conditional(HibernateEntityManagerCondition.class) . This checks if the JPA provider is Hibernate, this is done via the HibernateEntityManagerCondition.java. In our case it is true since Spring Boot brings in Hibernate as the default provider of JPA and HibernateEntityManager.java is on the classpath.

9.Once these conditions are satisfied, the HibernateJpaAutoConfiguration extends the JpaBaseConfiguration class.


@EnableConfigurationProperties(JpaProperties.class)
@Import(DataSourceInitializedPublisher.Registrar.class)
public abstract class JpaBaseConfiguration implements BeanFactoryAware {
}

This is annotated with the @EnableConfigurationProperties(JpaProperties.class).  This JpaProperties.class is annotated with @ConfigurationProperties(prefix = “spring.jpa”) . This gets triggered if we were to add any spring.jpa.xxx properties in our application.properties file. We would use this if we were to add any jpa specific properties. We did not add any and hence the default is assumed.

10.The JpaBaseConfiguration class contains all the other remaining configurations. It primarily uses the @ConditionalOnMissingBean annotation to configure the EntityManager, JpaVendorAdapter, LocalContainerEntityManagerFactoryBean, JpaTransactionManager if they have already not been configured. The @ConditionalOnMissingBean when applied on a method means – if this bean has not been configured, then execute the method and configure it.


@Bean
@ConditionalOnMissingBean(PlatformTransactionManager.class)
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
return transactionManager;
}

So in this way, datasource, entitymanager, vendor(hibernate) specific properties and transactions get configured. If you have setup a Spring Hibernate application before, we would do the same using Java configuration or an xml and configure the above mentioned properties. You can configure any specific properties and in that case Spring Boot will skip auto configuring that property.

Conclusion 

All the Harry Houdini illusions or Spring Boot magic starts from the spring.factories file and then through the usage of some powerful annotations like @ConditionalOnMissingBean, ConditionalOnClass,@AutoConfigureAfter , Jpa/Datasource/Transactions gets configured depending on what is found in the classpath. Spring Boot will not configure a property or a bean if it was already configured.

Complaining is a Habit ?

I have heard people complaining about the so-called magic in Spring Boot. Well, when Spring folks gave us xml configurations, we complained ! They gave us configurations via annotations and Java Configuration to get rid of the xml, we still complained ! Now, things get configured automatically…..and we still complain. Complaining can be good at times, the folks at Pivotal have been listening to our complaints and have been giving us fantastic tools/ frameworks. But let’s not forget that the details are out there, let’s explore a little more before complaining.

 

 

 

 

 

Spring Boot and Hibernate – Quick Introduction

It has been a while since I wrote this blog about how I  discovered Spring Boot. A surgery to fix my ACL and meniscus tear has kept me away from a lot of things, more about that in a different blog. But it is time we got a little intimate with Spring Boot.

In this blog we will look at how to setup a simple Spring Boot application with JPA/Hibernate. I remember shying away from writing/experimenting any code whenever I heard – setting up something with Spring. Those were the dark ages ! Well, if you haven’t tried the super cool Spring Initializr yet , it is high time you did. You can setup Spring based projects in an absolute flash !

All I did was enter JPA, H2 in the Search for dependencies  box and imported the project in my IDE.

spring-boot-jpa-intializr

Dependencies

The pom.xml from the imported project contains the following important dependencies:


<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

We added 2 things in the search box, JPA and H2 and so now the pom.xml contains dependencies corresponding to that:

  1. The spring-boot-started-data-jpa dependency brings in the latest version of the Hibernate, support for transactions, spring-data-jpa etc. Not only does it bring in the latest versions but also compatible versions. Notice that we need not add spring-orm and all the pain to setup the latest and compatible versions of Hibernate, JPA, transactions. This is done automatically for us.
  2. The h2 dependency above is for an embedded database that we will be using. One can easily replace that with the respective database in your project. Since h2 database will be present on the classpath, Spring Boot will configure the h2 database for us. If you add the dependency for Mysql database, Spring Boot will help configuring a MySql database.

JPA Entity

To keep things simple, we are going to create a Project entity which contains id, name and description and persist this  data. We will use Spring Data to persist and fetch the data.

Application starting point

The starting point in our application is the SpringBootJpaApplication.java:


package com.boot.jpa;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootJpaApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootJpaApplication.class, args);
}
}

The output from running the file above: (lines 4 – 8)


2017-06-06 00:56:10.523 INFO 616 — [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2017-06-06 00:56:10.812 INFO 616 — [ main] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory
2017-06-06 00:56:11.204 INFO 616 — [ main] com.boot.jpa.ProjectInitRunner : Project[ id = 1 , name = 'Java' , description = 'Java based project for a bank']
2017-06-06 00:56:11.205 INFO 616 — [ main] com.boot.jpa.ProjectInitRunner : Project[ id = 2 , name = 'Scala' , description = 'Scala based project for an oil company']
2017-06-06 00:56:11.206 INFO 616 — [ main] com.boot.jpa.ProjectInitRunner : Project[ id = 3 , name = 'Apache Kafka' , description = 'Kafka based project for a data mining company']
2017-06-06 00:56:11.206 INFO 616 — [ main] com.boot.jpa.ProjectInitRunner : Getting project by name
2017-06-06 00:56:11.268 INFO 616 — [ main] com.boot.jpa.ProjectInitRunner : Scala based project id 2

Sample Data added via the Initializer

In addition to the SpringBootJpaApplication.java, there is a ProjectInitRunner.java which is run by Spring Boot on startup. It contains initialization code which inserts some Project entities into the H2 database and then fetches one of the project details. This class is run on start up as it implements the CommandLineRunner interface. I am currently using it to persist some data into the H2 database.


package com.boot.jpa;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ProjectInitRunner implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(ProjectInitRunner.class);
@Autowired
private ProjectRepository projectRepository;
@Override
public void run(String… args) throws Exception {
// Save
projectRepository.save(new Project("Java", "Java based project for a bank"));
projectRepository.save(new Project("Scala", "Scala based project for an oil company"));
projectRepository.save(new Project("Apache Kafka", "Kafka based project for a data mining company"));
// Find all projects.
List<Project> projects = projectRepository.findAll();
projects.forEach(project -> logger.info(project.toString()));
// Get a project by name
logger.info("Getting project by name");
Project project = projectRepository.findByName("Scala");
logger.info("Scala based project id " + project.getId());
}
}

The Spring Boot starter data jpa brings in the Spring Data project which we are using to persist and retrieve the data.

Package structure

This is how the package structure looks :

spring-boot-sts-package

 

Gotchas – Packages and Spring  component scanning

Since all the classes were in the same package, the @SpringBootApplication annotation simply scanned and picked up all classes which were in the same package. This works as the @SpringBootApplication does Auto Configuration for us via the @EnableAutoConfiguration annotation. This would also work if other Spring beans were in sub or child packages.

If the Repository and the entities were in a different package with respect to the SpringBootJpaApplication.java which contains the main method, we need to do slightly more work:

spring-boot-sts-split-package

As shown above, Project.java and ProjectRepository.java are in a completely different package as compared to the class containing the main method. We need to now inform Spring Boot to enable/scan the repository and entity related classes. This is simply done by modifying the SpringBootJpaApplication.java and adding 2 annotations, @EnableJpaRepositories and @EntityScan. The annotations contain the information about packages that need to be scanned to enable the Jpa repository and scan the JPA entity related classes.No other change is needed and everything runs as usual.


package com.boot.jpa;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@SpringBootApplication
@EnableJpaRepositories(basePackages="com.boot.repository")
@EntityScan(basePackages = "com.boot.repository")
public class SpringBootJpaApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootJpaApplication.class, args);
}
}

Conclusion

So it is really quite simple and super quick to setup a Spring Boot JPA project. The starter projects brings in all the required dependencies and also provides us with default and most importantly right and latest configurations if we don’t specify any. We can always override the default configurations by adding our own and Spring Boot will happily skip that part.

I think in case of Spring Boot, we can really say – Less (no?) Pain, More Gain !

All the code can be found here : Github