Spring Data JPA Example with Spring Boot
1. Introduction
In this post, we shall demonstrate how to leverage the powerful Spring Data JPA APIs to interact with the database, in-memory H2 database for this lesson.
Spring Data JPA offers a set of very powerful and highly-abstracted interfaces which are used to interact with any underlying database. Databases can be MySQL, MongoDB, Elasticsearch or any other supported database. Other advantages for Spring Data JPA include:
- Support to build extended repositories based on JPA Convention
- In-built pagination support and dynamic query execution
- Support for XML based entity mapping
In this example, we will make use of H2 in-memory database. The choice for the database should not affect the Spring Data definitions we will construct as this is the main advantage Spring Data JPA offers. It enables us to completely separate the Database queries from the application logic.
2. Project Setup
We will be using one of the many Maven archetypes to create a sample project for our example. To create the project execute the following command in a directory that you will use as workspace:
mvn archetype:generate -DgroupId=com.javacodegeeks.example -DartifactId=JCG-SpringDataJPA-example -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
If you are running maven for the first time, it will take a few seconds to accomplish the generate command because maven has to download all the required plugins and artifacts in order to make the generation task.
Notice that now, you will have a new directory with the same name as the artifactId
inside the chosen directory. Now, feel free to open the project in your favourite IDE.
Finally, instead of using an IDE to make this project, we used a simple maven command. This helps us to make project setup and initialisation free from any specific IDE you may use.
3. Maven Dependencies
To start with, we need to add appropriate Maven dependencies to our project. We will add the following dependency to our pom.xml file:
pom.xml
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.10.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
Find the latest Spring related dependencies here.
Note that we have also added the H2 database dependency here as well with its scope as runtime as the H2 data is washed away as soon as the application has stopped. In this lesson, we will not focus on how H2 actually works but will restrict ourself to Spring Data JPA APIs. You may also see how we can Configure Embedded H2 Console With a Spring Application.
4. Project Structure
Before we move on and start working on the code for the project, let’s present here the projet structure we will have once we’re finished adding all code to the project:
We have divided the project into multiple packages so that the principle of separation of concern is followed and code remains modular.
5. Defining the Model
We will start by adding a very simple model in our project, a Person
. Its definition will be very standard, like:
Person.java
import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity public class Person { @Id @GeneratedValue private Long id; private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } //standard getters and setters @Override public String toString() { return String.format("Person{id=%d, name='%s', age=%d}", id, name, age); } }
We omitted standard getters and setters for brevity but they are necessary to be made as Jackson uses them during Serialization and Deserialization of an Object.
The @Entity
annotation marks this POJO as an object which will be managed by the Spring Data APIs and its fields will be treated as table columns (unless marked transient).
Finally, we added a custom implementation for the toString()
method so that we can print related data when we test our application.
6. Defining JPA Repository
JPA provides us with a very simple way of defining a JPA Repository interface.
Before getting to know how to define a JPA Repository, we need to remember that each JPA interface is only made to interact with a single Entity of Database Table when JPA-related functionality is leveraged. We will understand this deeply if we have a look at the interface definition:
PersonRepository.java
import com.javacodegeeks.jpaexample.model.Person; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface PersonRepository extends JpaRepository<Person, Long> { }
Although above interface definition is empty, we still have some points which we need to understand:
@Repository
annotation marks this interface as a Spring Bean which is initialised on application startup. With this annotation, Spring takes care of managing exception database interaction throws gracefuly- We used
Person
as a parameter to signify that this JPA interface will manage thePerson
Entity - Finally, we also passed the data type
Long
as a parameter. This signifies that thePerson
Entity contains a unique identifier which is of the typeLong
7. Making Service interface
In this section, we will define a service interface which will act as a contract for the implementation and represnet all the actions our Service must support. These actions will be related to making a new user and getting information related to the objects in database.
Here is the contract definition we will be using:
PersonService.java
import com.javacodegeeks.jpaexample.model.Person; import java.util.List; public interface PersonService { Person createPerson(Person person); Person getPerson(Long id); Person editPerson(Person person); void deletePerson(Person person); void deletePerson(Long id); List getAllPersons(int pageNumber, int pageSize); List getAllPersons(); long countPersons(); }
We have mentioned all four CRUD operations in this contract along with concept of Pagination.
A paginated API is important to make when we introduce getting all objects from the database based on a pageSize
and a pageNumber
. The pageSize
attribute signifies the number of objects which are fetched from the database whereas the pageNumber
attribute act as skip part of the query. For detailed lesson on how Pagination works in Spring, read this lesson.
8. Providing Service implementation
We will use the above interface definition to provide its implementation so that we can perform CRUD operations related to the Person
Entity we defined earlier. We will do it here:
PersonServiceImpl.java
import com.javacodegeeks.jpaexample.model.Person; import com.javacodegeeks.jpaexample.repository.PersonRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; import java.util.List; @Service public class PersonServiceImpl implements PersonService { @Autowired private PersonRepository personRepository; @Override public Person createPerson(Person person) { return personRepository.save(person); } @Override public Person getPerson(Long id) { return personRepository.findOne(id); } @Override public Person editPerson(Person person) { return personRepository.save(person); } @Override public void deletePerson(Person person) { personRepository.delete(person); } @Override public void deletePerson(Long id) { personRepository.delete(id); } @Override public List<Person> getAllPersons(int pageNumber, int pageSize) { return personRepository.findAll(new PageRequest(pageNumber, pageSize)).getContent(); } @Override public List<Person> getAllPersons() { return personRepository.findAll(); } @Override public long countPersons() { return personRepository.count(); } }
It is positively surprising that all the method implementations are only one line of code. This shows the level of abstraction JPA Repositories provide to us.
Most of the operations above are simple to understand. Main thing to notice is that we never provided any methods in the Repository we made like getAllPersons()
etc! Then how did these methods appeared altogether? The answer, again, lies in the abstraction JPA Repositories provide to us. All of the methods like findAll()
, delete()
, save(...)
etc. are in-built into the JpaRepository
we extended in our repository interface definition.
9. Using the CommandLineRunner
To test all the code we have written toll now, along with the database interactionpart, we will be using the CommandLineRunner
in our main class of our Spring Boot application. A CommandLineRunner
runs right before the main()
method for the Spring Boot application is called and so, it is an ideal space to perform any init steps or testing code.
To test the application, we will use a service bean to perform database operations in our class:
pom.xml
import com.javacodegeeks.jpaexample.model.Person; import com.javacodegeeks.jpaexample.service.PersonService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DataJpaApp implements CommandLineRunner { private static final Logger LOG = LoggerFactory.getLogger("JCG"); @Autowired private PersonService service; public static void main(String[] args) { SpringApplication.run(DataJpaApp.class, args); } @Override public void run(String... strings) { LOG.info("Current objects in DB: {}", service.countPersons()); Person person = service.createPerson(new Person("Shubham", 23)); LOG.info("Person created in DB : {}", person); LOG.info("Current objects in DB: {}", service.countPersons()); person.setName("Programmer"); Person editedPerson = service.editPerson(person); LOG.info("Person edited in DB : {}", person); service.deletePerson(person); LOG.info("After deletion, count: {}", service.countPersons()); } }
In above sample code, we just made simple calls to some important methods we created in our service like creating some data and accessing it later method calls.
Now, we will finally run our project using Maven itself (again being independent from any IDE to run our project).
10. Running the Application
Running the application is easy with maven, just use the following command:
Running the Application
mvn spring-boot:run
Once we run the project, we will be seeing the following output:
As expected, we first created some sample data and confirmed it by calling the count()
method call. Finally, we deleted the data and again confirmed it with the count()
method call.
11. Download the Source Code
This was an example of with Spring Boot and Spring Data JPA APIs along with the in-memory H2 database.
You can download the full source code of this example here: Spring Data JPA Example
Thanks for the tutorial.
In my case I have a data base ready for my application, so can you through some light on how to create a entity class for existing database structure?
Hello Vinod.
I will recommend by starting to define simple POJOs related to the database structure because even POJOs should be describing the schema you want to exploit.
Next, to understand how you can use JPA related relationship annotations on these POJOs, read this lesson: https://examples.javacodegeeks.com/enterprise-java/jpa/jpa-relationship-annotations-example/ and start adding annotations one at a time so that you don’t get messed up in the annotation web.
Please feel free to get back if you need more insight.
I downloaded the example code and imported it into Eclipse Oxygen as a Maven project. Immediately I got the following error on the POM.xml file:
Project build error: Non-resolvable parent POM for com.javacodegeeks.example:JCG-SpringDataJPA-Example:1.0-SNAPSHOT: Could not transfer artifact org.springframework.boot:spring-boot-starter-parent:pom:1.5.10.RELEASE from/to central (https://repo.maven.apache.org/maven2): repo.maven.apache.org: unknown error and ‘parent.relativePath’ points at no local POM
Do you know what caused it? Any help will be greatly appreciated.
Thanks!
Line 6 in PersonRepository.java
public interface PersonRepository extends JpaRepository {
should be
public interface PersonRepository extends JpaRepository {
“JpaRepository “
Missing Person,Long after JpaRepository inside angled brackets. Content inside angled brackets is being removed in this blog.
true, as “We used Person as a parameter to signify that this JPA interface will manage the Person Entity” lacks code
Thanks for pointing this out Santosh. Code has been updated now.
How can I configure jpa with other database (not in-memory)? Thanks