首页 > 代码库 > Spring Data JPA Tutorial: CRUD
Spring Data JPA Tutorial: CRUD
http://www.petrikainulainen.net/programming/spring-framework/spring-data-jpa-tutorial-part-two-crud/
We have now configured the persistence layer of our Spring application. We are finally ready to create our first Spring Data JPA repository.
This blog post describes how we can create a repository that provides CRUD operations for todo entries.
Let’s get started.
If you are not familiar with Spring Data JPA, you should read the following blog posts before you continue reading this blog post:
- Spring Data JPA Tutorial: Introduction provides a quick introduction to Spring Data JPA and gives an overview of the Spring Data repository interfaces.
- Spring Data JPA Tutorial: Getting the Required Dependencies describes how you can get the required dependencies.
- Spring Data JPA Tutorial: Configuration describes how you can configure the persistence layer of a Spring application that uses Spring Data JPA.
Creating the Repository
Before we can create our first Spring Data JPA repository, we have to create an entity class that contains the information of a single todo entry. The relevant part of the Todo class looks as follows:
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 39 | import org.hibernate.annotations.Type; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.PrePersist; import javax.persistence.Table; import javax.persistence.Version; import java.time.ZonedDateTime; @Entity @Table (name = "todos" ) final class Todo { @Id @GeneratedValue (strategy = GenerationType.AUTO) private Long id; @Column (name = "creation_time" , nullable = false ) @Type (type = "org.jadira.usertype.dateandtime.threeten.PersistentZonedDateTime" ) private ZonedDateTime creationTime; @Column (name = "description" , length = 500 ) private String description; @Column (name = "modification_time" ) @Type (type = "org.jadira.usertype.dateandtime.threeten.PersistentZonedDateTime" ) private ZonedDateTime modificationTime; @Column (name = "title" , nullable = false , length = 100 ) private String title; @Version private long version; //The constructor, builder, and other methods are omitted } |
- Hibernate Reference Documentation: 6.4 Custom Types
- The Javadoc of the PersistentZonedDateTime class
We are now ready to create our first Spring Data JPA repository. We can create the repository that provides CRUD operations for Todo objects by using one of the following methods:
- Create an interface that extends the CrudRepository interface.
- Create an interface that extends the Repository interface and add the required methods to the created interface.
Let’s take a closer look at these methods.
Extending the CrudRepository Interface
If we create our repository by extending the CrudRepository interface, we have to provide two type parameters:
- The type of the entity that is managed by our repository.
- The type of the entity’s id field.
In other words, when we create the repository that provides CRUD operations for Todo objects, we have to provide the following type parameters:
- The type of the entity is Todo.
- The type of the entity’s id field is Long.
The source code of the TodoRepository interface looks as follows:
1 2 3 4 5 | import org.springframework.data.repository.CrudRepository; interface TodoRepository extends CrudRepository<Todo, Long> { } |
- The Javadoc of the CrudRepository interface
- Spring Data JPA Reference Manual: 2.3 Defining Repository Interfaces
The CrudRepository interface declares many methods, but the methods that are relevant for this blog post are described in the following:
- The void delete(T entity) method deletes the entity whose id is given as a method parameter.
- The Iterable<T> findAll() method returns all entities that are saved to the database.
- The T findOne(Long id) method returns the entity whose id is given as method parameter. If no entity is found, this method returns null.
- The T save(T entity) method saves the entity given as a method parameter and returns the persisted entity.
Let’s find out how we can create a repository interface that extends the Repository interface.
Extending the Repository Interface
If we create our repository by extending the Repository interface, we have to follow these steps:
- Provide two type parameters:
- The type of the managed entity (Todo).
- The type of the entity’s id field (Long).
- Add the required methods to the repository interface:
- The void delete(Todo deleted) method deletes the Todo object given as a method parameter.
- The List<Todo> findAll() method returns all Todo objects that are found from the database.
- The Optional<Todo> findOne(Long id) method finds the todo entry whose id is given as a method parameter. If no todo entry is found, this method returns an empty Optional.
- The Todo save(Todo persisted) method saves the Todo object given as a method parameter and returns the persisted object.
The source code of the TodoRepository interface looks as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import org.springframework.data.repository.Repository; import java.util.List; import java.util.Optional; interface TodoRepository extends Repository<Todo, Long> { void delete(Todo deleted); List<Todo> findAll(); Optional<Todo> findOne(Long id); Todo save(Todo persisted); } |
Additional Reading:
- The Javadoc of the Repository interface
- Spring Data JPA Reference Manual: 2.3.1 Fine-tuning repository definition
- What’s New in Spring Data Dijkstra (search for ‘support for wrapper types as return values’)
Let’s move on and find out which method we should use.
Which Method Should We Use?
It depends.
I know that this is probably the most annoying answer one can give to a question. That is why I created two rules that we can follow when we are creating Spring Data JPA repositories. These rules are:
- If we want to expose all repository methods that are declared by the CrudRepository interface AND we don’t want to return Optional (Guava / Java 8) objects, our repository interfaces should extend the CrudRepository interface.
- If we don’t want to expose all repository methods that are declared by the CrudRepository interface OR we want to return Optional (Guava / Java 8) objects, our repository interfaces must extend the Repository interface.
Case closed?
Not exactly. I argue that we should always use the second method. This opinion is based on two reasons:
- When we create an interface, we should not add unnecessary methods to it. We should keep the interface as small as possible because small interfaces are easier to use and they help us to create components that have only one job.
- Optional helps us to create better APIs because it reveals that there might not be a return value.
- Minimal Interface
- Intention Revealing Code With Java 8’s New Type ‘Optional’
- Tired of Null Pointer Exceptions? Consider Using Java SE 8’s Optional
- The Design of Optional
- Java 8 Optional Objects
If we create our repositories by extending the Repository interface and adding the required methods to the created repository interfaces, we need to add the “same” methods to every interface. Right?
Wrong.
We can avoid this by following these steps:
- Create a base interface that extends the Repository interface and add the common methods to that interface.
- Create the actual repository interface that extends our base interface.
Let’s move on and take a closer look at these steps.
First, we have to create a base interface that declares the methods shared by our repositories. We can do this by following these steps:
- Create the BaseRepository interface that extends the Repository interface. This interface has two type parameters:
- T describes the type of the managed entity.
- ID describes the type of the entity’s id field.
- Annotate the created interface with the @NoRepositoryBean annotation. This ensures that Spring Data JPA doesn’t try to create an implementation for our base repository interface.
- Add the common methods to the created interface.
The source code of the BaseRepository interface looks as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import org.springframework.data.repository.NoRepositoryBean; import org.springframework.data.repository.Repository; import java.util.List; import java.util.Optional; @NoRepositoryBean interface BaseRepository<T, ID extends Serializable> extends Repository<T, ID> { void delete(T deleted); List<T> findAll(); Optional<T> findOne(ID id); T save(T persisted); } |
- The Javadoc of the @NoRepositoryBean annotation
Second, we have to create the actual repository interface that extends our base interface. We can do this by following these steps:
- Create the TodoRepository interface.
- Extend the BaseRepository interface and provide two type parameters:
- The type of the managed entity is Todo.
- The type of the entity’s id field is Long.
The source code of the TodoRepository interface looks as follows:
1 2 3 | interface TodoRepository extends BaseRepository<Todo, Long> { } |
We have now created a repository hierarchy that allows us to:
- Create repositories that provides CRUD operations for entities without declaring the “same” methods in every repository interface.
- Create repositories that do not provide all CRUD operations. For example, we can create a repository that provides only the findAll() method.
The following figure illustrates the benefits of this solution:
Let’s move on and summarize what we learned from this blog post.
Summary
This blog post has taught us three things:
- We can create repository interfaces by extending either the CrudRepository or the Repository interface.
- We should create our repositories by extending the Repository interface and adding the required methods to the created repository interface.
- If our application has more than one repository, we should create a base repository interface that declares the methods that are shared by our “concrete” repositories.
The next part of this tutorial describes how we can create database queries by using query methods.
P.S. You can get the example application of this blog post from Github.
Spring Data JPA Tutorial: CRUD