SpringBoot JPA example with H2 DB

ne key feature of Spring is its support for the Java Persistence API (JPA), which is a Java specification for accessing, persisting, and managing data between Java objects/classes and a database. In this tutorial, we will learn how to use JPA with Spring Boot to build robust and efficient applications.

Setting up the Project

Firstly, we will kickstart the Spring Boot project. The simplest way to do that is by means of the Spring Initializr online application.

Add the following dependencies, as you can see from the following picture:

  • Spring Data JPA
  • H2 Database ( or the Database you are using)
  • Spring Web

spring jpa example

Alternatively, if you are using the Spring CLI, you can init the project as follows:

$ spring init -dweb,data-jpa,h2   samplewebapp

Finally, by opening your project configuration, you should have the following dependencies in place:



As next step, import the project in your IDE before we add our Classes in it.

Defining the Entity

An entity in JPA represents a table in the database. To create an entity, we need to create a Java class and annotate it with @Entity. Each field in the class represents a column in the table. The following code defines an entity called Person with three fields: id, name, and surname:

public class Person {
    @GeneratedValue(strategy = GenerationType.AUTO)
    Long id;
    String name;
    String surname;

    public Person(String name, String surname) {
        this.name = name;
        this.surname = surname;

    // Getter / Setters omitted for brevity

The following Class Diagram summarizes your Entity definition:

spring boot jpa example

@GeneratedValue is a JPA annotation that you can use to specify how to generate the value for the primary key field. The strategy attribute of @GeneratedValue specifies the generation strategy to use.

The GenerationType.AUTO strategy lets the persistence provider choose an appropriate strategy for the particular database. This is useful when you want to use a single set of code across multiple databases, and each database has its own way of generating primary keys.

JPA Classes

The Java Persistence API classes to use very depending on your Spring Boot versions. If using Spring Boot 2, you will need to import javax.persistence packages. On the other hand, Spring Boot 3 applications need to import the jakarta.persistence packages in the Entity Class.

Creating the repository

The repository is a layer between the database and the application that provides an interface for performing CRUD (create, read, update, delete) operations on the Person entity. To create a repository, we need to create an interface that extends the JpaRepository interface. The JpaRepository interface provides methods for performing basic CRUD operations and pagination.

public interface PersonRepository extends JpaRepository<Person, Long> {
    List<Person> findBySurname(String surname);

As you can see from the above code, we are including only an extra method findBySurname to search a Person by surname.

Creating the service

The service layer is responsible for managing transactions and handling business logic. To create a service, we need to create an interface and its implementation. The following code defines a PersonService Class with a savePerson method for saving a Person object to the database:

public class PersonService   {
    private PersonRepository personRepository;

    public Person savePerson(Person person) {
        return personRepository.save(person);

    public List<Person> findAll() {
        return personRepository.findAll();

    public Optional<Object> findById(long l) {
        return Optional.of(personRepository.findById(l));

    public Optional<List<Person>> findBySurname(String surname) {
        return Optional.of(personRepository.findBySurname(surname));

The PersonService class has several methods for performing operations on Person objects.

  • savePerson: takes a Person object as an argument and saves it to the repository using the save method of PersonRepository.
  • findAll: retrieves all Person objects from the repository using the findAll method of PersonRepository.
  • findById: takes a long value as an argument and returns an Optional object containing the Person object with the matching ID, using the findById method of PersonRepository.
  • findBySurname: takes a String value as an argument and returns an Optional object containing a list of Person objects with the matching surname, using the findBySurname method of PersonRepository.

Optional in the Service Class

  • By returning an Optional allows the caller of the method to check if a value is present before attempting to access it. This can prevent NullPointerExceptions from being thrown if the value is null. It makes the intention of the method clearer.

Creating the controller

The controller is responsible for handling incoming HTTP requests and returning the appropriate response.

This controller defines several endpoints for performing CRUD operations on Person objects. The @RestController annotation indicates that this class is a controller and the @RequestMapping annotation specifies the base path for the endpoints defined in the class.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

public class PersonController {
  private final PersonService personService;

  public PersonController(PersonService personService) {
    this.personService = personService;

  public Person savePerson(@RequestBody Person person) {
    return personService.savePerson(person);

  public List<Person> findAll() {
    return personService.findAll();

  public Optional<Object> findById(@PathVariable long id) {
    return personService.findById(id);

  public Optional<List<Person>> findBySurname(@PathVariable String surname) {
    return personService.findBySurname(surname);

Each endpoint simply calls the corresponding method of the PersonService class to perform the desired operation and returns the result to the client.

Coding the Application Class

Finally, we will add an Application Class which contains the @SpringBootApplication in it. We will show how to peform a basic Test run from within the Application Class using the CommandLineRunner. The CommandLineRunner bean is useful for running code that should be executed when the application starts up. It

public class DemoApplication {
  private static final Logger log = LoggerFactory.getLogger(DemoApplication.class);

  public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  public CommandLineRunner demo(PersonService personService) {
    return (args) -> {
      // save a couple of persons
      personService.savePerson(new Person("Jack", "Smith"));
      personService.savePerson(new Person("Joe", "Black"));
      personService.savePerson(new Person("Martin", "Bauer"));

      // fetch all persons
      log.info("Persons found with findAll():");
      for (Person person : personService.findAll()) {

      // fetch an individual person by ID
          .ifPresent(person -> {
            log.info("Person found with findById(1L):");

      log.info("Person found with findBySurname('Black'):");

      Optional<List<Person>> people = personService.findBySurname("Smith");
      if (people.isPresent()) {
        people.get().forEach(person -> {


Then, you can run your application by simply right-clicking on your main Class from the IDE. For example:

spring boot jpa h2 database

Alternatively, you can also run it as follows:

mvn clean install spring-boot:run


java -jar target/demo-0.0.1-SNAPSHOT.jar

Finally, here is the output provided by the SpringApplication class:

Persons found with findAll():
Person [id=1, name=Jack, surname=Smith]
Person [id=2, name=Joe, surname=Black]
Person [id=3, name=Martin, surname=Bauer]

Person found with findById(1L):
Optional[Person [id=1, name=Jack, surname=Smith]]

Person found with findBySurname('Black'):
Optional[[Person [id=1, name=Jack, surname=Smith]]]

Testing the JPA application using the REST Controller

In this section we will provide some curl scripts to Test the JPA Application using the curl command line tool.

Firstly, save a new Person object:

curl -X POST -H "Content-Type: application/json" -d '{"name": "John", "surname": "Doe"}' http://localhost:8080/persons

Then, retrieve a list of all Person objects:

curl http://localhost:8080/persons

Next, fetch a single Person object by ID:

curl http://localhost:8080/persons/1

Finally, retrieve a list of Person objects by surname:

curl http://localhost:8080/persons/surname/Doe

Testing the Entity with a Spring Boot Test Class

Finally, we will add a Test Class to test the PersonService using JUnit 5 and AssertJ libraries:

class PersonServiceTest {
  private PersonService personService;

  public void testSavePerson() {
    Person person = new Person("John", "Doe");
    Person savedPerson = personService.savePerson(person);

  public void testFindAll() {
    List<Person> persons = personService.findAll();

  public void testFindById() {
    Optional<Object> person = personService.findById(1L);

  public void testFindBySurname() {
    Optional<List<Person>> persons = personService.findBySurname("Doe");

Run the above example from the IDE and verify that all Service methods are passing the test:

spring boot h2 jpa tutorial


In conclusion, Spring Boot with JPA is a powerful combination for developing web applications quickly and efficiently. With JPA, it’s easy to map Java objects to database tables and execute complex queries with just a few lines of code. And the H2 database is a lightweight, in-memory database that is well-suited for development and testing purposes. Together, Spring Boot, JPA, and H2 make it easy to build and deploy robust, data-driven applications.


Spring Boot 2 JPA: https://github.com/fmarchioni/masterspringboot/tree/master/jpa/samplewebapp

Source Boot 3 JPA: https://github.com/fmarchioni/masterspringboot/tree/master/jpa/samplewebapp3