Spring Data JPA using Hibernate and Java Configuration with Annotations

In this tutorial, I’ll show you how to use Spring Data JPA to integrate a relational database (PostgreSQL in my example) into a Spring Boot application.

Spring Data JPA using Hibernate and Java Configuration - Thomas Vitale

I’ll use these technologies and tools:

  • Spring Tool Suite (STS) 3.8.4.RELEASE
  • Java 8
  • Spring Boot 1.5.3.RELEASE
  • Maven 3.3.9
  • PostgreSQL 9.6.2

1. The Project Structure

The final folder structure of our project.

The folder structure of the Spring Data JPA project as seen in STS

2. Create a new Spring Boot project

If you’re using STS, you can create a starter project by either selecting File > New > Spring Starter Project from the main menu or right-clicking on the Package Explorer and choose New > Spring Starter Project.

STS command to create a new Spring Starter Project

Dialog window to set parameters for the Spring Data JPA project

The list of dependencies of the Spring Data JPA project

In case you’re using another IDE like Eclipse, Netbeans or IntelliJ IDEA, you can create a new Maven project and add to the pom.xml file the dependencies listed in the next paragraph.

3. Maven Dependencies

spring-boot-starter-data-jpa is the critical dependency of our project. Then we need the driver for whatever SQL database we have chosen, in this case postgresql.

<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.thomasvitale</groupId>
  <artifactId>Application</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>Application</name>
  <description>Demo application for Spring Data JPA</description>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.3.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
  </properties>

  <dependencies>
  
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <dependency>
      <groupId>org.postgresql</groupId>
      <artifactId>postgresql</artifactId>
      <scope>runtime</scope>
    </dependency>
  
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>

</project>

I’m using PostgreSQL, but this example works fine also with other relational databases. For instance, if you want to use MySQL, all you need to do is replace the postgresql dependency with the MySQL one.

<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <scope>runtime</scope>
</dependency>

4. Spring Configuration using Java Annotations and Hibernate

Spring lets you use either Java configuration or XML configuration or a mix of the two. I’ll use a pure Java configuration. There are a few implementations of JPA: in this example, I’ll use Hibernate. Its properties are stored in a dedicated file.

package com.thomasvitale;

import java.util.Properties;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement 
@EnableJpaRepositories(basePackages = "com.thomasvitale.repository")
@PropertySource("classpath:jpa.properties")
public class JpaConfig {
  
  @Autowired
  private Environment env;

  @Bean
  public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
    
    HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    vendorAdapter.setDatabase(Database.POSTGRESQL);
    vendorAdapter.setGenerateDdl(true);
    
    LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
    em.setDataSource(dataSource());
    em.setPackagesToScan("com.thomasvitale.model");
    em.setJpaVendorAdapter(vendorAdapter);
    em.setJpaProperties(additionalProperties());
 
    return em;
  }
  
  @Bean
  public DataSource dataSource(){
    DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setDriverClassName(env.getProperty("hibernate.connection.driver_class"));
    dataSource.setUrl(env.getProperty("hibernate.connection.url"));
    dataSource.setUsername(env.getProperty("hibernate.connection.username"));
    dataSource.setPassword(env.getProperty("hibernate.connection.password"));
    return dataSource;
  }
  
  @Bean
  public PlatformTransactionManager transactionManager(EntityManagerFactory emf){
    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManager.setEntityManagerFactory(emf);
  
    return transactionManager;
  }
  
  @Bean
  public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
    return new PersistenceExceptionTranslationPostProcessor();
  }

  Properties additionalProperties() {
    Properties properties = new Properties();
    properties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
    properties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
    properties.setProperty("hibernate.current_session_context_class", env.getProperty("hibernate.current_session_context_class"));
    return properties;
  }

}
hibernate.connection.username=db-user
hibernate.connection.password=db-password
hibernate.connection.url=jdbc:postgresql://localhost:5432/app
hibernate.connection.driver_class=org.postgresql.Driver
hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
hibernate.current_session_context_class=thread
hibernate.hbm2ddl.auto=validate
hibernate.show_sql=false
hibernate.format_sql=false

If you’re using MySQL, pay attention to replace line 36 of JpaConfig.java with this one: vendorAdapter.setDatabase(Database.MYSQL); and change the relative values in the jpa.properties file:

hibernate.connection.url=jdbc:mysql://localhost:3306/app
hibernate.connection.driver_class=com.mysql.jdbc.Driver
hibernate.dialect=org.hibernate.dialect.MySQLDialect

5. Person Model

For the Person entity, first of all, I create a new table (persons) in my PostgreSQL database (called app).

CREATE TABLE persons(
  id SERIAL,
  firstName VARCHAR(32),
  lastName VARCHAR(32),
  PRIMARY KEY(id)
);

Then I code the mapping object model. The @Table(name = "persons") annotation specifies in which table to save a Person object. The @GeneratedValue(strategy = GenerationType.IDENTITY) is quite general. In PostgreSQL, I have defined the id attribute as SERIAL. In MySQL, you probably want to use AUTO_INCREMENT. That’s just fine; it works with both of them.

package com.thomasvitale.model;

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

@Entity
@Table(name = "persons")
public class Person {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;
  
  private String firstName;
  private String lastName;

  public Person() {
  }

  public Person(String firstName, String lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  public Integer getId() {
    return id;
  }

  public void setId(Integer 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;
  }

  @Override
  public String toString() {
    return firstName + " " + lastName;
  }
}

6. Person Repository

Our PersonRepository extends the JpaRepository interface, which provides us with a lot of operations out-of-the-box, including the standard CRUD operations. You can also add custom operations like findByFirstName and findByLastName without writing any implementation: Spring Data JPA creates them for you!

package com.thomasvitale.repository;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.thomasvitale.model.Person;

@Repository
public interface PersonRepository extends JpaRepository<Person, Integer> {
  
  public Person findByFirstName(String firstName);
  public List<Person> findByLastName(String lastName);
  
}

7. Demo

Let’s try some CRUD operations to test our application.

package com.thomasvitale;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.thomasvitale.model.Person;
import com.thomasvitale.repository.PersonRepository;

@SpringBootApplication
public class Application implements CommandLineRunner {
  
  @Autowired
  PersonRepository personRepository;

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }

  @Override
  public void run(String... arg0) throws Exception {
    
      // Save three Person documents on PostgreSQL
      personRepository.save(new Person("Sheldon", "Cooper"));
      personRepository.save(new Person("Missy", "Cooper"));
      personRepository.save(new Person("Leonard", "Hofstadter"));
      
      // Get all the people
      System.out.println(">>> All the people in the database:");
      personRepository.findAll().forEach(System.out::println);
      
      // Get all the people with a specific last name
      System.out.println(">>> All people with last name = 'Cooper'");
      personRepository.findByLastName("Cooper").forEach(System.out::println);
      
      // Update an individual person
      Person person = personRepository.findByFirstName("Sheldon");
      person.setFirstName("Shelly");
      personRepository.save(person);
      
      // Delete all 
      personRepository.deleteAll();
    
  }
}

Resources

Leave a Reply

Your email address will not be published. Required fields are marked *