Tutorial

Spring Data JPA

Published on August 3, 2022
author

Pankaj

Spring Data JPA

Spring Data JPA is part of Spring Data family. Spring Data makes it easier to create Spring driven applications that use new ways to access data, such as non-relational databases, map-reduction frameworks, cloud services, as well as well-advanced relational database support. This article will discuss about Spring Data JPA. We will also look into Spring Data JPA example application.

Spring Data JPA

Some of the cool features provided by Spring Data JPA are:

  1. Create and support repositories created with Spring and JPA
  2. Support QueryDSL and JPA queries
  3. Audit of domain classes
  4. Support for batch loading, sorting, dynamical queries
  5. Supports XML mapping for entities
  6. Reduce code size for generic CRUD operations by using CrudRepository

When to use Spring Data JPA?

I would say that if you need to quickly create a JPA-based repository layer that is mainly for CRUD operations, and you do not want to create abstract DAO, implementing interfaces, Spring Data JPA is a good choice.

Spring Data JPA Example

For our Spring Data JPA example, we will create a RESTful web service that will connect to Postgresql database. We will implement basic CRUD operations and work on a sample data we already have created.

Spring JAP Example Sample Data

Use below query to create table in Postgresql database and add some test data.

create table people (
id serial not null primary key,
first_name varchar(20) not null,
last_name varchar(20) not null,
age integer not null
);

insert into people (id, first_name, last_name, age) values
(1, 'Vlad', 'Boyarskiy', 21),
(2,'Oksi', ' Bahatskaya', 30),
(3,'Vadim', ' Vadimich', 32);

Spring Data JPA Maven Project Structure

Below image shows the final Spring JPA project structure. We will look into each of the components in detail later on. Spring Data JPA, Spring JPA Example

Spring Data JPA Maven Dependencies

We need to add following dependencies for our Spring Data JPA example project.

  1. postgresql: Postgresql java driver.
  2. spring-core, spring-context: Spring Framework Core dependencies.
  3. spring-webmvc, jackson-databind: For Spring REST application.
  4. spring-data-jpa, hibernate-entitymanager: for Spring Data JPA and Hibernate support.

Below is content of final pom.xml build file.

<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.journaldev</groupId>
	<artifactId>springData</artifactId>
	<packaging>war</packaging>
	<version>1.0-SNAPSHOT</version>
	<name>Spring Data JPA Maven Webapp</name>
	<url>https://maven.apache.org</url>
	<properties>
		<spring.framework>4.3.0.RELEASE</spring.framework>
		<postgres.version>42.1.4</postgres.version>
		<serializer.version>2.8.1</serializer.version>
		<spring.data>1.3.4.RELEASE</spring.data>
		<hibernate.manager>4.2.5.Final</hibernate.manager>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.framework}</version>
		</dependency>
		<dependency>
			<groupId>org.postgresql</groupId>
			<artifactId>postgresql</artifactId>
			<version>${postgres.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${spring.framework}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.framework}</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-jpa</artifactId>
			<version>${spring.data}</version>
		</dependency>

		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>${hibernate.manager}</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.5</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>${serializer.version}</version>
		</dependency>
	</dependencies>

	<build>
		<finalName>${project.artifactId}</finalName>
	</build>
</project>

Spring Configuration Classes

package com.journaldev.spring.config;

import java.util.Properties;

import javax.sql.DataSource;

import org.hibernate.ejb.HibernatePersistence;
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.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.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories("com.journaldev.spring.repository")
@PropertySource("classpath:database.properties")
public class DataConfig {

	private final String PROPERTY_DRIVER = "driver";
	private final String PROPERTY_URL = "url";
	private final String PROPERTY_USERNAME = "user";
	private final String PROPERTY_PASSWORD = "password";
	private final String PROPERTY_SHOW_SQL = "hibernate.show_sql";
	private final String PROPERTY_DIALECT = "hibernate.dialect";

	@Autowired
	Environment environment;

	@Bean
	LocalContainerEntityManagerFactoryBean entityManagerFactory() {
		LocalContainerEntityManagerFactoryBean lfb = new LocalContainerEntityManagerFactoryBean();
		lfb.setDataSource(dataSource());
		lfb.setPersistenceProviderClass(HibernatePersistence.class);
		lfb.setPackagesToScan("com.journaldev.spring.model");
		lfb.setJpaProperties(hibernateProps());
		return lfb;
	}

	@Bean
	DataSource dataSource() {
		DriverManagerDataSource ds = new DriverManagerDataSource();
		ds.setUrl(environment.getProperty(PROPERTY_URL));
		ds.setUsername(environment.getProperty(PROPERTY_USERNAME));
		ds.setPassword(environment.getProperty(PROPERTY_PASSWORD));
		ds.setDriverClassName(environment.getProperty(PROPERTY_DRIVER));
		return ds;
	}

	Properties hibernateProps() {
		Properties properties = new Properties();
		properties.setProperty(PROPERTY_DIALECT, environment.getProperty(PROPERTY_DIALECT));
		properties.setProperty(PROPERTY_SHOW_SQL, environment.getProperty(PROPERTY_SHOW_SQL));
		return properties;
	}

	@Bean
	JpaTransactionManager transactionManager() {
		JpaTransactionManager transactionManager = new JpaTransactionManager();
		transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
		return transactionManager;
	}
}
  • @Configuration: this spring annotation says that it is configuration class.

  • @EnableTransactionManagement: this annotation allows users to use transaction management in application.

  • @EnableJpaRepositories("com.journaldev.spring.repository"): indicates where the repositories classes are present.

  • @PropertySource("classpath:database.properties"): says that we have property file in our classpath. The values from this file will be injected into environment variable. The contents of the database.properties file are shown below.

    driver=org.postgresql.Driver
    url=jdbc:postgresql://127.0.0.1:5432/postgres
    user=postgres
    password=postgres
    
    hibernate.dialect=org.hibernate.dialect.PostgreSQL82Dialect
    hibernate.show_sql=true
    
  • For using Spring Data, first of all we have to configure DataSource bean. Then we need to configure LocalContainerEntityManagerFactoryBean bean. We need this bean to control the entities. In this beans, you must specify the persistence provider i.e. HibernatePersistence in our case.

  • The next step is to configure bean for transaction management. In our example it’s JpaTransactionManager. Note that without configuring transaction manager we can’t use @Transactional annotation.

AppInitializer and WebConfig classes are to configure our application as web application without using web.xml file.

Model Class

package com.journaldev.spring.model;

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

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

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;

	@Column(name = "age")
	private Integer age;
	@Column(name = "first_name")
	private String firstName;
	@Column(name = "last_name")
	private String lastName;

	public Person() {
	}

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	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 "Person{" + "id=" + id + ", age=" + age + ", firstName='" + firstName + '\'' + ", lastName='" + lastName
				+ '\'' + '}';
	}
}

Here we have a few new annotations. Let’s talk about them in more detail.

  • @Entity: This annotation allows entity manager to use this class and puts it in context.
  • @Table(name = “people”): associates a class with a table in the database.
  • @Id: says that this field is the primary key.
  • @GeneratedValue(strategy = GenerationType.IDENTITY): Defines the strategy for generating the primary key.
  • @Column(name = "age"): denotes a column in the database with which this field will be associated.

Spring Data JPA Repository

Next step is to create the JPA repository.

package com.journaldev.spring.repository;

import org.springframework.data.repository.CrudRepository;

import com.journaldev.spring.model.Person;

import java.util.List;

public interface PersonRepository<P> extends CrudRepository<Person, Long> {
    List<Person> findByFirstName(String firstName);
}

By inheriting from CrudRepository, we can call many methods without the need to implement them ourself. Some of these methods are:

  • save
  • findOne
  • exists
  • findAll
  • count
  • delete
  • deleteAll

We can also define our own methods. These method names should use special keywords such as “find”, “order” with the name of the variables. Spring Data JPA developers have tried to take into account the majority of possible options that you might need. In our example findByFirstName(String firstName) method returns all entries from table where field first_name equals to firstName. This is one of the most important feature of Spring Data JPA because it reduces a lot of boiler plate code. Also the chances of errors are less because these Spring methods are well tested by many projects already using them.

Spring Service Class

Now that our Spring Data JPA code is ready, next step is to create service class and define methods that we will have to work with database table.

package com.journaldev.spring.services;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.journaldev.spring.model.Person;
import com.journaldev.spring.repository.PersonRepository;

@Service
public class PersonService {

	@Autowired
	PersonRepository<Person> personRepository;

	@Transactional
	public List<Person> getAllPersons() {
		return (List<Person>) personRepository.findAll();
	}

	@Transactional
	public List<Person> findByName(String name) {
		return personRepository.findByFirstName(name);
	}

	@Transactional
	public Person getById(Long id) {
		return personRepository.findOne(id);
	}

	@Transactional
	public void deletePerson(Long personId) {
		personRepository.delete(personId);
	}

	@Transactional
	public boolean addPerson(Person person) {
		return personRepository.save(person) != null;
	}

	@Transactional
	public boolean updatePerson(Person person) {
		return personRepository.save(person) != null;
	}
}

@Transactional annotation indicates that the method will be executed in the transaction. Spring will take care of transaction management.

Spring Controller Class

Final step is to create the controller class to expose our APIs to outer world.

package com.journaldev.spring.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.journaldev.spring.model.Person;
import com.journaldev.spring.services.PersonService;

@RestController
public class PersonController {

	@Autowired
	PersonService personService;

	@RequestMapping(value = "/person/{id}", method = RequestMethod.GET)
	public @ResponseBody Person getAllUsers(@PathVariable Long id) {
		return personService.getById(id);
	}

	@RequestMapping(value = "/personByName/{name}", method = RequestMethod.GET)
	public List<Person> getPersoneByName(@PathVariable String name) {
		return personService.findByName(name);
	}

	@RequestMapping(value = "/person", method = RequestMethod.GET)
	public List<Person> getAll() {
		return personService.getAllPersons();
	}

	@RequestMapping(value = "/person/{id}", method = RequestMethod.DELETE)
	public HttpStatus deletePersnone(@PathVariable Long id) {
		personService.deletePerson(id);
		return HttpStatus.NO_CONTENT;
	}

	@RequestMapping(value = "/person", method = RequestMethod.POST)
	public HttpStatus insertPersone(@RequestBody Person person) {
		return personService.addPerson(person) ? HttpStatus.CREATED : HttpStatus.BAD_REQUEST;
	}

	@RequestMapping(value = "/person", method = RequestMethod.PUT)
	public HttpStatus updatePerson(@RequestBody Person person) {
		return personService.updatePerson(person) ? HttpStatus.ACCEPTED : HttpStatus.BAD_REQUEST;
	}
}

Spring Data JPA Testing

Just build and deploy the project into your favorite servlet container such as Tomcat. Below images show the response for some of the API calls.

Spring Data JPA Read All

Spring Data JPA Example Read All

Spring Data JPA Get By Name

Spring JPA Read

Spring Data JPA Create

Spring Data JPA Create

Spring Data JPA Update

Spring Data JPA Update

Spring Data JPA Delete

Spring Data JPA Example Delete That’s all for Spring Data JPA example tutorial. You can download the final project from below link.

Download Spring Data JPA Example Project

Reference: Official Website

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the authors
Default avatar
Pankaj

author

While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
JournalDev
DigitalOcean Employee
DigitalOcean Employee badge
January 7, 2021

Hi Pankaj, I have to keep the sql Queries at one place and provide the SQL query from that central constants… How can we do that. I tried with two approach 1. Keeping all the SQL in one class as public static final variable, But getting error at run time, Can you please confirm In this case the Query must be native??? 2. Kept all the query at property file but fetching it from there I am always getting error at location value (“Attribute value must be constant”) @Component @Repository public interface QueryRepo extends JpaRepository, JpaSpecificationExecutor { @Query(value = findAllTags2) public List findAllTags(@Param(“list”) Set names); } Please provode me the solution, Thanks a lot in advance!!!

- Spandan

    JournalDev
    DigitalOcean Employee
    DigitalOcean Employee badge
    November 27, 2020

    pankaj can u post junit testing for my service class for get findByDate(Date TodaysDate) method

    - shivani

      JournalDev
      DigitalOcean Employee
      DigitalOcean Employee badge
      August 16, 2020

      Hi, i want to add another entity lets say address, and i need to save it in that case do i need to create separate repository and service class?

      - Navami

        JournalDev
        DigitalOcean Employee
        DigitalOcean Employee badge
        July 7, 2020

        It should extend JpaRepository and not CrudRepository. Please reply

        - Deepak Lalchandani

          JournalDev
          DigitalOcean Employee
          DigitalOcean Employee badge
          January 10, 2020

          How to set JPA transaction timeout through hibernate properties in above example. Not through @Transactional (timeout) attribute.

          - Durga Prasad

            JournalDev
            DigitalOcean Employee
            DigitalOcean Employee badge
            September 30, 2019

            How can make auto create table funactionality in your project

            - Saurabh Tiwari

              JournalDev
              DigitalOcean Employee
              DigitalOcean Employee badge
              August 19, 2019

              This is awesome tutorial, always love your articles. Thanks Pankaj !

              - krutik

                JournalDev
                DigitalOcean Employee
                DigitalOcean Employee badge
                December 17, 2018

                Getting error not able to up app at tomcat server. Using oracle database.

                - Sahil Khan

                  JournalDev
                  DigitalOcean Employee
                  DigitalOcean Employee badge
                  December 4, 2018

                  What if I am pushing same records again. How to handle duplicacy.

                  - Shashank Singh

                    JournalDev
                    DigitalOcean Employee
                    DigitalOcean Employee badge
                    September 28, 2018

                    Error creating bean with name ‘personController’: Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘personService’ Getting the following issue. Please suggest.

                    - Rudra

                      Try DigitalOcean for free

                      Click below to sign up and get $200 of credit to try our products over 60 days!

                      Sign up

                      Join the Tech Talk
                      Success! Thank you! Please check your email for further details.

                      Please complete your information!

                      Become a contributor for community

                      Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

                      DigitalOcean Documentation

                      Full documentation for every DigitalOcean product.

                      Resources for startups and SMBs

                      The Wave has everything you need to know about building a business, from raising funding to marketing your product.

                      Get our newsletter

                      Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.

                      New accounts only. By submitting your email you agree to our Privacy Policy

                      The developer cloud

                      Scale up as you grow — whether you're running one virtual machine or ten thousand.

                      Get started for free

                      Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

                      *This promotional offer applies to new accounts only.