Sample Spring Data JPA application.

February 26, 2018

In this post I am going to demonstrate common methods and approaches of working with Spring Data JPA. Spring Data JPA is a part of the big family Spring Data. Spring Data which is an umbrella project and the most popular project in set of Spring Tools. This project allows us to build fully configuring the persistence layer. I am going to demonstrate and do some code explanation of boilerplate code.

Spring Boot logo

Spring Data JPA provides an implementation of the data access layer for Spring applications. The main goal of Spring Data and Spring Data JPA in particular is to significantly reduce the amount of boilerplate code required to implement data access layers for various persistence stores. So in several words if you familiar with DAO pattern by using Spring Data JPA you almost don’t need to implement DAO interfaces and it is now the only artifact that needs to be explicitly defined.

Spring Data Core Concepts

Spring Data has several core concepts/abstractions which in general were borrowed from DAO pattern and JPA specification.

Entity

Entities - Simple and lightweight java class. I would say Entity is Java Bean or Domain Object which is reflecting particular Data Base table. Entities usually have relationships with other entities and might be expressed/specified through object/relational metadata by using annotations or XML descriptor file. Here an example of definition of entity:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Entity // 1
@Table // 2
public class Task extends BaseEntity implements Serializable {

    @Id // 3
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String title;

    private String description;

    private Boolean isComplete;

    @ManyToOne  // 4
    private TodoList todoList;

    // ... Constructors

    /// ... Getters/Setters
}
  1. We use @Entity to define Entity Spring Data JPA provides a ClasspathScanningPersistenceUnitPostProcessor to scan packages for classes annotated with @Entity or @MappedSuperclass.
  2. @Table JPA annotation. Specifies the primary table for the annotated entity.
  3. Another JPA annotation Specifies the primary key of an entity.
  4. This class has relation with another Entity - TodoList. And Type of relationship is Many To One

Repository

Repositories is central part of Spring Data abstraction. In several words repositories are interfaces that you can define to access data. Here an example of definition of repository:

1
2
3
4
5
6
7
8
@Repository // 1
public interface TaskRepository extends 
    CrudRepository<Task, Long>,  // 2
    PagingAndSortingRepository<Task, Long> { // 3
    
    List<Task> findByTitle(String title); // 4
    Page<Task> findByTitle(String title, Pageable pageable);
}
  1. @Repository Indicates that this interface is a Repository
  2. This interface extends CrudRepository for generic CRUD operations on a repository for a specific type. It takes Entity type and type of ID(primary key) of Entity.
  3. We extend one more interface PagingAndSortingRepository which is extension of CrudRepository and provides additional methods to retrieve entities using the pagination and sorting abstraction.
  4. Simple method for searching by title. Spring Data JPA automatically pick up name of this method, which use special naming convention - Query Methods, and provides/injects implementation. So we don’t need to implement logic of querying data from DB.

Naming Convention for repositories

To consume all Spring Data features, and allowing Spring Framework inject implementation of Query Methods we have to fallow naming convention for repository interfaces and methods. We should call our repositories next format: <EntityClassName>Repository eg: TaskRepository, TodoListRepository etc. in that case we even do not need to use @Repository annotation to indicate that interface is a Repository

Using Repositories

To use repository from your Service or Controller you need to define member of your class with type of repository you are going to use and indicate this field (member) with @Autowired annotation. Your class also has to be indicated by @Component annotation, or annotation derived from @Component like @Service, @Controller.

1
2
3
4
5
6
7
@Service
public class TodoService {

    @Autowired
    protected TaskRepository taskRepository;
    ...
}

Now you can call method of your repository inside your class methods. here is common CRUD methods:

  • taskRepository.save(task) - Saves a given entity
  • taskRepository.save(tasks) - Saves all given entities
  • taskRepository.findOne(yaskId) - Retrieves an entity by its id
  • taskRepository.exists(taskId) - Returns whether an entity with the given id exists. true if an entity with the given id exists
  • taskRepository.findAll() - Returns all entities. Use carefully if you operate big amount of data
  • taskRepository.findAll(TaskIds) - Returns all instances of the type with the given IDs
  • taskRepository.count() - Returns the number of entities available
  • taskRepository.delete(taskId) - Deletes the entity with the given id
  • taskRepository.delete(task) - Deletes a given entity
  • taskRepository.delete(tasks) - Deletes the given entities
  • taskRepository.deleteAll() - Deletes all entities managed by the repository

entity - an instance of Task class.

When you are saving new entity into DB and ID of this entity is Auto Generated - @GeneratedValue(strategy = GenerationType.AUTO) you don’t need to set value for ID manually. Do it ONLY in case if you want to update exists entity.

Project description

To show features of Spring Data JPA I decided to implement Todo REST API which use almost all Spring Data JPA functional and relatively close to real production application.

Project structure

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
└── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── alexbezverkhniy
│   │   │           └── samples
│   │   │               └── springdatajpasample
│   │   │                   ├── SpringDataJpaSampleApplication.java
│   │   │                   ├── controllers
│   │   │                   │   └── TodoController.java
│   │   │                   ├── domain
│   │   │                   │   ├── BaseEntity.java
│   │   │                   │   ├── Task.java
│   │   │                   │   └── TodoList.java
│   │   │                   ├── repositories
│   │   │                   │   ├── TaskRepository.java
│   │   │                   │   └── TodoListRepository.java
│   │   │                   └── services
│   │   │                       ├── TaskNotFoundException.java
│   │   │                       ├── TodoListNotFoundException.java
│   │   │                       └── TodoService.java
│   │   └── resources
│   │       ├── application.yml
│   │       ├── static
│   │       └── templates
│   └── test
│       └── java
│           └── com
│               └── alexbezverkhniy
│                   └── samples
│                       └── springdatajpasample
│                           ├── SpringDataJpaSampleApplicationTests.java
│                           └── services
│                               ├── TodoServiceIntegTest.java
│                               └── TodoServiceTest.java
├── README.md
├── build.gradle
└── pom.xml
  • SpringDataJpaSampleApplication.java - Main application
  • BaseEntity.java - Base Super class for all Entities
  • Task.java, TodoList.java - Entities
  • TaskRepository.java, TodoListRepository.java - Repositories
  • TodoService.java - Main Servers with general logic
  • TodoController.java - REST Controller for working via HTTP/REST
  • application.yml - Has DB connection settings

As you see project has only two related entities Task.java, TodoList.java which extend one common super class - BaseEntity.java. By extending BaseEntity.java I want to show how can we define common fields, audit fields: createdAt, updatedAt, createdBy, updatedBy.

Entities Diagram

In project we use next Domain structure:

Entities Diagram

DataBase Configuration

This is String Boot project and all configurations you can find in application.yml file. Here is what we have in that file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
spring:
  application:
    name: todo-api
  datasource:
    url: jdbc:h2:mem:todoDB;DB_CLOSE_DELAY=-1
    driver-class-name: org.h2.Driver
    username: sa
    password:

---

spring:
  profiles: test
  datasource:
    url: jdbc:h2:tcp://localhost/~/todoDB
    driver-class-name: org.h2.Driver
    username: sa
    password:

As you can project has only two profiles default and test. By default we use embedded H2 database and for test profile I use H2 database as standalone server.

Run application and call endpoints

As I mentioned above this project is just REST interface for database and I use Spring Boot and embedded Tomcat server to lift up REST controllers and handle HTTP requests.

Run application

Before run application you need to build this project

Build with Maven

1
mvn clean install

In case of successful build you should expect this message and should see file spring-data-jpa-sample-0.0.1-SNAPSHOT.jar file under target folder.

1
2
3
4
5
6
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 10.244 s
...

Build with Gradle

1
gradle clean build

In case of successful build you should expect this message and should see file spring-data-jpa-sample-0.0.1-SNAPSHOT.jar file in build/libs.

1
2
3
4
...
BUILD SUCCESSFUL in 14s
8 actionable tasks: 8 executed
...

Run Application

To run application use next command java -jar target/spring-data-jpa-sample-0.0.1-SNAPSHOT.jar or java -jar build/libs/spring-data-jpa-sample-0.0.1-SNAPSHOT.jar depending on which tool you use for building.

Call application

Now we can call our application and do some “smoke test”. Here are some examples how can we call through curl

Create Task

Task data:

1
2
3
4
5
{
  "title": "Sample Task",
  "description": "Just simple task",
  "isComplete": false
}
1
2
3
4
5
6
7
8
9
curl -X POST \
-H 'Content-Type: application/json' \
-d '{
      "title": "Sample Task",
      "description": "Just simple task",
      "isComplete": false
    }' \
'http://localhost:8080/api/tasks/' \
| python -m json.tool

Read Task

1
curl -X GET 'http://localhost:8080/api/tasks/1' | python -m json.tool

Update Task

Task data:

1
2
3
4
5
{
  "title": "Sample Task",
  "description": "Just simple task",
  "isComplete": false
}
1
2
3
4
5
6
7
8
9
curl -X PUT \
-H 'Content-Type: application/json' \
-d '{
      "title": "Sample Task",
      "description": "Just simple task",
      "isComplete": false
    }' \
'http://localhost:8080/api/tasks/1' \
| python -m json.tool

You can find source code of this project in my github repository. Welcome to ask questions in comments.

Written on February 26, 2018
java spring-boot spring-data jpa cheat-sheet maven gradle microservices