Sample Spring Data JPA application.
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 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
}
- We use
@Entity
to define Entity Spring Data JPA provides a ClasspathScanningPersistenceUnitPostProcessor to scan packages for classes annotated with@Entity
or@MappedSuperclass
. @Table
JPA annotation. Specifies the primary table for the annotated entity.- Another JPA annotation Specifies the primary key of an entity.
- 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);
}
@Repository
Indicates that this interface is a Repository- 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.
- We extend one more interface PagingAndSortingRepository which is extension of CrudRepository and provides additional methods to retrieve entities using the pagination and sorting abstraction.
- 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 entitytaskRepository.save(tasks)
- Saves all given entitiestaskRepository.findOne(yaskId)
- Retrieves an entity by its idtaskRepository.exists(taskId)
- Returns whether an entity with the given id exists. true if an entity with the given id existstaskRepository.findAll()
- Returns all entities. Use carefully if you operate big amount of datataskRepository.findAll(TaskIds)
- Returns all instances of the type with the given IDstaskRepository.count()
- Returns the number of entities availabletaskRepository.delete(taskId)
- Deletes the entity with the given idtaskRepository.delete(task)
- Deletes a given entitytaskRepository.delete(tasks)
- Deletes the given entitiestaskRepository.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 applicationBaseEntity.java
- Base Super class for all EntitiesTask.java
,TodoList.java
- EntitiesTaskRepository.java
,TodoListRepository.java
- RepositoriesTodoService.java
- Main Servers with general logicTodoController.java
- REST Controller for working via HTTP/RESTapplication.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:
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.