What is Spring Data JPA? And why should you use it?

When you implement a new application, you should focus on the business logic instead of technical complexity and boilerplate code. That’s why the Java Persistence API (JPA) specification and Spring Data JPA are extremely popular. JPA handles most of the complexity of JDBC-based database access and object-relational mappings. On top of that, Spring Data JPA reduces the amount of boilerplate code required by JPA. That makes the implementation of your persistence layer easier and faster.

Spring Data JPA adds a layer on top of JPA. That means it uses all features defined by the JPA specification, especially the entity and association mappings, the entity lifecycle management, and JPA’s query capabilities. On top of that, Spring Data JPA adds its own features like a no-code implementation of the repository pattern and the creation of database queries from method names.

3 reasons to use Spring Data JPA

OK, so if the JPA specification and its implementations provide most of the features that you use with Spring Data JPA, do you really need the additional layer? Can’t you just use the Hibernate or EclipseLink directly?

You can, of course, do that. That’s what a lot of Java SE applications do. Jakarta EE provides a good integration for JPA without adding an extra layer.

But the Spring Data team took the extra step to make your job a little bit easier. The additional layer on top of JPA enables them to integrate JPA into the Spring stack seamlessly. They also provide a lot of functionality that you otherwise would need to implement yourself.

Here are my 3 favorite features that Spring Data adds on top of JPA.

1. No-code Repositories

The repository pattern is one of the most popular persistence-related patterns. It hides the data store specific implementation details and enables you to implement your business code on a higher abstraction level.

Implementing that pattern isn’t too complicated but writing the standard CRUD operations for each entity creates a lot of repetitive code. Spring Data JPA provides you a set of repository interfaces which you only need to extend to define a specific repository for one of your entities.

I will show you Spring Data repositories in more details at the end of this article. Here is a quick example of a repository that provides the required methods:

And before you ask: Yes, that code sample is correct and complete. The CrudRepository interface defines all methods I mentioned before. So, you just need to extend it.

2. Reduced boilerplate code

To make it even easier, Spring Data JPA provides a default implementation for each method defined by one of its repository interfaces. That means that you no longer need to implement basic read or write operations. And even so all of these operations don’t require a lot of code, not having to implement them makes life a little bit easier and it reduces the risk of stupid bugs.

3. Generated queries

Another comfortable feature of Spring Data JPA is the generation of database queries based on method names. As long as your query isn’t too complex, you just need to 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.

Here is a simple example of a query that loads a Book entity with a given title. Internally, 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.

Using Spring Data JPA with Spring Boot

As you have seen, Spring Data JPA can make the implement of your persistence layer much easier. So, what do you have to do to use it in your application? Not much, if you’re using Spring Boot and structure your application in the right way.

You only need to add the spring-boot-starter-data-jpa artifact, and your JDBC driver to your maven build. The Spring Boot Starter includes all required dependencies and activates the default configuration.

In the next step, you can configure your database connection in the application.properties or application.yml file. If you use JPA outside of Spring, you need to configure this and a few other things in the persistence.xml. Spring Boot and Spring Data JPA handle the default configuration for you, so that you only need to override the parameters you want to change.

If you structure your project in the right way, that’s all you need to do to be able to use Spring Data JPA and its repositories in your project. By default, Spring Boot expects that all repositories are located in a sub-packages of the class annotated with @SpringBootApplication. If your application doesn’t follow this default, you need to configure the packages of your repositories using an @EnableJpaRepositories annotation.

Repositories in Spring Data JPA

After setting everything up, it’s time to take a closer look at repositories. There are 3 repository interfaces that you should know when you use Spring Data JPA:

As you might guess from its name, the CrudRepository interface defines a repository that offers standard create, read, update and delete operations. The PagingAndSortingRepository extends the CrudRepository and adds findAll methods that enable you to sort the result and to retrieve it in a paginated way. Both interface are also supported by other Spring Data projects, so that you can apply the same concepts to different datastores. The JpaRepository adds JPA-specific methods, like flush() to trigger a flush on the persistence context or findAll(Example<S> example) to find entities by example, to the PagingAndSortingRepository.

Defining an entity-specific repository

You can use any of the standard interfaces to define your own repository definition. You, therefore, need to extend one of Spring Data JPA’s interface, e.g. the CrudRepository interfaces and type it to the entity class and its primary key class.

If you want to define a CRUD repository for this entity, you need to extend Spring Data JPA’s CrudRepository interface and type it to Book and Long. In the following code snippet, I also added the findByTitle method to the repository to find a Book entity by a given title.

Working with Repositories

After you defined your repository interface, you can use the @Autowired annotation to inject it into your service implementation. Spring Data will then provide you with a proxy implementation of your repository interface. This proxy provides default implementations for all methods defined in the interface. If you need to adapt the default functionality, you can provide your own repository implementations. But that a topic for another article. Let’s focus on Spring Data JPA’s standard functionality for now.

In your business code, you can then use the injected repository to read entities from the database and to persist new or changed entities. The test class in the following code snippet uses the BookRepository to find a Book entity with the title ‘Hibernate Tips’ and to persist a new Book entity.

Conclusion

Spring Data JPA seamlessly integrates JPA into the Spring stack, and its repositories reduce the boilerplate code required by the JPA specification.

It’s important to know that most features, like the object-relational mapping and query capabilities, are defined and provided by the JPA specification and its implementations. That means that you can use all the features of your favorite JPA implementation. Spring Data JPA just makes using them easier.