Spring Data is a popular project which provides an easy-to-use and consistent, Spring-based programming model for storing and retrieving data from a data store. This allows you to connect with different data access technologies, relational and non-relational databases, map-reduce frameworks, and cloud-based data services.
Spring Data JPA (Java Persistence API) is an extension of Spring Data that makes it easy to connect with relational databases to implement JPA-based repositories.
What is Java Persistence API?
Java Persistence API is a specification that defines an object-relational mapping (ORM) standard for storing, accessing, and managing Java objects in a relational database.
While originally intended for use with relational/SQL databases only, the ORM model of Spring Data JPA has been since extended for use with NoSQL data stores.Right now, two most popular implementations of JPA specification are Hibernate and EclipseLink.
Spring Data JPA is not a JPA provider but just an extra layer of abstraction on top of an existing JPA provider such as Hibernate. This means it uses all features defined by the JPA specification, such as the entity and association mappings, the entity lifecycle management, and JPA query capabilities.
On top of this, Spring Data JPA defines its powerful features, such as no-code repositories and the ability to generate queries based on method names. Thus, eliminating the need for writing too much boilerplate code for executing simple queries.
Why Spring Data JPA?
Although we can use any JPA implementation like Hibernate or EclipseLink directly in our project, Spring Data JPA usage gives us additional benefits. This significantly reduces the boilerplate code and makes the overall development much faster.
The extra layer on top of the JPA specification also allows us to build Spring-powered applications that use JPA for the data access layer.
Here is a list of features that makes the Spring Data JPA a go-to choice:
1. Repository Abstraction
The real strength of Spring Data JPA lies in the repository abstraction provided by the Spring Data Commons project. It hides the data-store-specific implementation details and allows you to write your business logic code on a higher abstraction level.
You only need to learn how to use Spring Data repository interfaces without worrying about the underlying implementation of the repository abstraction.
Spring Data Commons provides the following repository interfaces:
Repository
— Central repository marker interface. Captures the domain type and the ID type.CrudRepository
— Interface for generic CRUD operations on a repository for a specific type.PagingAndSortingRepository
— Extension ofCrudRepository
to provide additional methods to retrieve entities using the pagination and sorting abstraction.QuerydslPredicateExecutor
— Interface to allow execution of QueryDSL Predicate instances. It is not a repository interface.
Spring Data JPA provides the following additional repository interfaces:
JpaRepository
— JPA specific extension ofRepository
interface. It combines all methods declared by the Spring Data Commons repository interfaces behind a single interface.JpaSpecificationExecutor
— It is not a repository interface. It allows the execution of Specifications based on the JPA criteria API.
Here is a quick example of a repository that extends the CrudRepository
interface:
public interface UserRepository extends CrudRepository<User, Long> {}
Things you can do by using the above UserRepository
interface:
- Persist, update and remove one or multiple
User
entities. - Find one or more users by their primary keys.
- Count, get, and remove all users.
- Check if a
User
exists with a given primary key.
You might wonder how it is possible when we did not write any such method. The answer is the CrudRepository
interface defines all these methods I mentioned before and makes them available for you.
2. Less Boilerplate Code
The best thing about Spring Data JPA is the default implementation of each method defined in its repository interfaces. You do not need to write a boilerplate code for CRUD methods. You only need to extend the JPA-specific repository interface — JpaRepository
, and that is it. Spring Data JPA will ensure that CRUD methods are available for standard data access.
3. Auto-Generated Queries
Another cool feature of Spring Data JPA is the auto-generation of database queries based on method names. When you implement a Spring Data JPA repository interface, it analyses all the methods defined by the entity class and tries to generate the queries automatically from the method names.
Auto-generated queries may not be well-suited for complex use cases. But for simple scenarios, these queries are valuable. You just define a method on your repository interface with a name that starts with find...By
. Spring then parses the method name and creates a query for it automatically.
Following is an example of a query that loads a User
entity with a given name
. Behind the scene, Spring generates a JPQL query based on the method name, sets the provided method parameters as bind parameter values, executes the query, and returns the result:
public interface UserRepository extends CrudRepository<User, Long> {
User findByName(String name);
}
Using Spring Data JPA with Spring Boot
As discussed above, Spring Data JPA makes your data access layer implementation easier by reducing the boilerplate code. So that you build a Spring-based application using any data access technology.
In this section, I shall show you how to add and configure Spring Data JPA in a Spring Boot application using Hibernate as a persistence provider.
Follow the below steps to add Spring Data JPA support to your Spring Boot project.
Step 1 — Add Dependencies
You only need the spring-boot-start-data-jpa
dependency to enable JPA support in a Spring Boot application. Add the following dependency to your build.gradle
file:
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
If you are using Maven, add the following dependencies to your pom.xml
file:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Spring Boot Starter Data JPA includes everything — all required dependencies and activates the default configuration. To start a Spring Boot application from scratch, use the Spring Initializr tool to bootstrap your application with the required dependencies.
Step 2 — Configure Data Source
Spring Boot automatically configures Hibernate as a default JPA provider. It no longer requires explicit configuration of the EntityManagerFactory
bean unless we want to customize it.
Similarly, if you use an in-memory database such as H2, HSQLDB, or Apache Derby, Spring Boot will auto-configure the DataSource
bean for you as well. You only need to ensure that the corresponding database dependency is available in the classpath.
H2 Configuration
Just add the following H2 database dependency to your Gradle project build.gradle
file:
runtimeOnly 'com.h2database:h2'
For Maven, add the following dependency to your pom.xml
file:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
That is it. Spring Boot will automatically configure the DataSource
bean for you when it detects the H2 database dependency in the classpath.
MySQL Configuration
To use the MySQL database with Spring Data JPA, you need to do a bit of extra work. First of all, add the following MySQL driver dependency to your build.gradle
file:
runtimeOnly 'mysql:mysql-connector-java'
For the Maven project, include the following dependency to your pom.xml
file:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
Next, you need to define the DataSource
configuration. There are two ways to do this. You can either define it as a bean in a Java class with the @Configuration
annotation or use the Spring Boot properties file to define data source properties.
Here is what the Java-based data source configuration looks like in a Spring Boot project:
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword("rootpass");
dataSource.setUrl("jdbc:mysql://localhost:3306/testdb?createDatabaseIfNotExist=true&useSSL=false");
return dataSource;
}
To configure the data source using a properties file, you need to define the following properties in your application.properties
or application.yml
file:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=rootpass
spring.datasource.url=jdbc:mysql://localhost:3306/testdb?createDatabaseIfNotExist=true&useSSL=false
Spring Boot will automatically configure a DataSource
bean based on the above properties.
Step 3 — Define an Entity
Let us define a simple entity class for storing User
objects:
User.java
package com.attacomsian.jpa.domains;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String email;
public User() {
}
public User(String name, String email) {
this.name = name;
this.email = email;
}
// getters and setters removed for brevity
}
The above User
class has three attributes (id
, name
, and email
) and two constructors. The no-argument constructor is only required for the JPA. The other constructor is the one you should use to create instances of User
to be saved to the database.
The User
class is annotated with @Entity
, indicating that it is a JPA entity. Since no @Table
annotation is provided, it assumes that this entity is mapped to a table named User
.
The id
property is annotated with @Id
so that JPA recognizes it as the object ID. The id
property is also annotated with @GeneratedValue
to indicate that the ID should be generated automatically.
The other two properties, name
and email
, are left unannotated. It means they are mapped to database table columns having the same names as the properties.
Step 4 — Create a Repository
The next step is to create a repository interface for the above User
entity:
UserRepository.java
package com.attacomsian.jpa.repositories;
import com.attacomsian.jpa.domains.User;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
public interface UserRepository extends CrudRepository<User, Long> {
List<User> findByName(String name);
User findByEmail(String email);
}
UserRepository
extends the CrudRepository
interface provided by the Spring Data Commons project. The type of entity and ID that it works with, User
and Long
, are specified in the generic parameters on CrudRepository
. By extending CrudRepository
, UserRepository
inherits several methods for saving, deleting, and finding User
entities.
Spring Data JPA also allows you to define other query methods by declaring their method signature. For example, UserRepository
declares two additional methods: findByName()
and findByEmail()
.
In a typical Java application, you need to write a class that implements UserRepository
interface methods. However, it is no longer required with Spring Data JPA. It will create the repository implementation automatically, at runtime, from the repository interface. That is what makes Spring Data JPA so much powerful.
Step 4 — Create an Application Class
Let us create the Spring Boot main application class to play with the above UserRepository
interface. Since we don't need the web part of Spring Boot, we will implement the CommandLineRunner
interface to make it a console application.
Application.java
package com.attacomsian.jpa;
import com.attacomsian.jpa.domains.User;
import com.attacomsian.jpa.repositories.UserRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class Application {
private static final Logger log = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public CommandLineRunner userDemo(UserRepository userRepository) {
return (args) -> {
// create users
userRepository.save(new User("John Doe", "john.doe@example.com"));
userRepository.save(new User("Emma Watson", "emma.watson@example.com"));
userRepository.save(new User("Seno Reta", "seno.reta@example.com"));
userRepository.save(new User("Mike Hassan", "mike.hassan@example.com"));
// fetch all users
log.info("Users found with findAll():");
log.info("---------------------------");
for (User user : userRepository.findAll()) {
log.info(user.toString());
}
log.info("");
// fetch user by id
User user = userRepository.findById(1L).get();
log.info("User found with findById(1L):");
log.info("-----------------------------");
log.info(user.toString());
log.info("");
// fetch user by email address
User userWithEmail = userRepository.findByEmail("john.doe@example.com");
log.info("User found with findByEmail('john.doe@example.com'):");
log.info("----------------------------------------------------");
log.info(userWithEmail.toString());
log.info("");
// delete all users
userRepository.deleteAll();
// confirm users deletion
log.info("Total users after deletion with :");
log.info("--------------------------");
log.info(userRepository.count() + " users are in DB");
log.info("");
};
}
}
The above class is pretty much self-explanatory. We used the @SpringBootApplication
annotation on the main class to activate the auto-configuration. The main()
method uses Spring Boot’s SpringApplication.run()
method to launch an application.
We have also defined a CommandLineRunner
bean method that gets UserRepository
on runtime through dependency injection. Inside this method, we first create and save some users via the save()
method. Next, we call findAll()
to fetch all User
objects from the database. Then it calls findById()
and findByEmail()
methods to get a single User
by its ID and email address respectively. Finally, it calls deleteAll()
to remove all users from the database. To verify that all users are successfully deleted, we call the count()
method to get the number of entities.
Note: By default, Spring Boot enables JPA repository support and looks in the package (and its sub-packages) where the class with the
@SpringBootApplication
annotation is located. If your application doesn't follow the default project structure, you need to configure your repositories package using the@EnableJpaRepositories
annotation.
Step 5 — Run the Application
You can run the application from the command line through Gradle or Maven. If you use Gradle, you can run the application by typing:
$ ./gradlew bootRun
If you use Maven, you can run the application by running the following command:
$ ./mvnw spring-boot:run
Once the application is started, you should see the following output:
Users found with findAll():
---------------------------
User{id=1, name='John Doe', email='john.doe@example.com'}
User{id=2, name='Emma Watson', email='emma.watson@example.com'}
User{id=3, name='Seno Reta', email='seno.reta@example.com'}
User{id=4, name='Mike Hassan', email='mike.hassan@example.com'}
User found with findById(1L):
-----------------------------
User{id=1, name='John Doe', email='john.doe@example.com'}
User found with findByEmail('john.doe@example.com'):
----------------------------------------------------
User{id=1, name='John Doe', email='john.doe@example.com'}
Total users after deletion with :
--------------------------
0 users are in DB
Source code: Download the complete source code from GitHub available under MIT license.
Conclusion
In this article, I introduced you to Spring Data, Spring Data JPA, and Java Persistence API. We also talked about the core features of Spring Data JPA that make it such a powerful tool.
Finally, we created a simple application to demonstrate how to configure and use Spring Data JPA in a Spring Boot application for storing, accessing, and deleting objects in an in-memory H2 database.
Further Reading
You may be interested in the following Spring Data JPA articles:
- Spring Data JPA with H2 DataBase and Spring Boot
- Accessing Data with Spring Data JPA and MySQL
- Derived Query Methods in Spring Data JPA
- Spring Data JPA Custom Queries with the @Query Annotation
- How to Use Spring Data JPA Named Queries
✌️ Like this article? Follow me on Twitter and LinkedIn. You can also subscribe to RSS Feed.