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.

Additional Reading:

 

If you are not familiar with Spring Data JPA, you should read the following blog posts before you continue reading this blog post:

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
}

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:

  1. Create an interface that extends the CrudRepository interface.
  2. 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:

  1. The type of the entity that is managed by our repository.
  2. 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:

  1. The type of the entity is Todo.
  2. 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 CrudRepository interface declares many methods, but the methods that are relevant for this blog post are described in the following:

The service class that provides CRUD operations for todo entries uses these methods for fulfilling its responsibilities.

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:

  1. Provide two type parameters:
    1. The type of the managed entity (Todo).
    2. The type of the entity’s id field (Long).
  2. Add the required methods to the repository interface:
    1. The void delete(Todo deleted) method deletes the Todo object given as a method parameter.
    2. The List<Todo> findAll() method returns all Todo objects that are found from the database.
    3. 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.
    4. 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);
}
If we don’t want to return Optional (Guava / Java 8) objects, we can also use the “traditional” Todo findOne(Long id) method.

 

Additional Reading:

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.

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:

  1. Create a base interface that extends the Repository interface and add the common methods to that interface.
  2. 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:

  1. Create the BaseRepository interface that extends the Repository interface. This interface has two type parameters:
    1. T describes the type of the managed entity.
    2. ID describes the type of the entity’s id field.
  2. 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.
  3. 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);
}

Second, we have to create the actual repository interface that extends our base interface. We can do this by following these steps:

  1. Create the TodoRepository interface.
  2. Extend the BaseRepository interface and provide two type parameters:
    1. The type of the managed entity is Todo.
    2. 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:

springdatajpabaserepository

The example application of this blog post does not use this approach because it has only one repository.

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.

posted @ 2015-01-14 07:23  testglen  阅读(515)  评论(0编辑  收藏  举报