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

By Thorben Janssen


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.

Sounds great, doesn’t it? It’s no surprise that a lot of development teams use that stack to implement their persistence layer.

If you’re new to it, I’m happy to introduce you to Spring Data JPA in this article. I will:

And I already posted a Getting started guide to JPA and Hibernate. If you’re not familiar with the JPA specification, please read that other guide first.

The relationship between Spring Data JPA, JPA, and Hibernate/EclipseLink

Before we start talking about Spring Data JPA, we should first discuss its relationship to JPA, Hibernate, and EclipseLink.

Watch it on YouTube
https://youtu.be/GNN-FpNucmI?rel=0

JPA is a specification that defines an API for object-relational mappings and for managing persistent objects. Hibernate and EclipseLink are 2 popular implementations of this specification. You can learn more about the difference in What’s the difference between JPA, Hibernate and EclipseLink

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:

  • to persist, update and remove one or multiple Author entities,
  • to find one or more Authors by their primary keys,
  • to count, get and remove all Authors and
  • to check if an Author with a given primary key exists.
package org.thoughts.on.java.spring.data.repository;

import org.springframework.data.repository.CrudRepository;
import org.thoughts.on.java.spring.data.model.Author;

public interface AuthorRepository extends CrudRepository<Author, Long> {}

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.

public interface BookRepository extends CrudRepository<Book, Long> {
	
	Book findByTitle(String title);
}

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.

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
	<groupId>org.postgresql</groupId>
	<artifactId>postgresql</artifactId>
	<scope>test</scope>
</dependency>

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.

spring.datasource.url = jdbc:postgresql://localhost:5432/recipes
spring.datasource.username = postgres
spring.datasource.password = postgres

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.

Let’s take a look at a simple example. The Book entity is a normal JPA entity with a generated primary key of type Long, a title and a many-to-many association to the Author entity.

@Entity
public class Book {

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private Long id;

	@Version
	private int version;

	private String title;

	@ManyToMany
	@JoinTable(name = "book_author", 
				joinColumns = { @JoinColumn(name = "fk_book") }, 
				inverseJoinColumns = { @JoinColumn(name = "fk_author") })
	private List authors = new ArrayList();
	
	...
}

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.

public interface BookRepository extends CrudRepository<Book, Long> {
	
	Book findByTitle(String 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.

@RunWith(SpringRunner.class)
@SpringBootTest
public class GettingStartedApplicationTests {

	Logger log = Logger.getLogger(this.getClass().getName());

    @Autowired
    private BookRepository bookRepository;

    
    @Test
    @Transactional
    public void testByTitle() {
        log.info("... testByTitle ...");

        Book b = bookRepository.findByTitle("Hibernate Tips");
        Assert.assertEquals(new Long(1), b.getId());
    }
    
    @Test
    @Transactional
    public void testPersistBook() {
        log.info("... testPersistBook ...");

        Book b = new Book();
        b.setTitle("Hibernate Tips - More than 70 solutions to common Hibernate problems");

        bookRepository.save(b);
    }
}

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.


Tags


About the author

Thorben is an independent consultant, international speaker, and trainer specialized in solving Java persistence problems with JPA and Hibernate.
He is also the author of Amazon’s bestselling book Hibernate Tips - More than 70 solutions to common Hibernate problems.

Books and Courses

Coaching and Consulting

Leave a Repl​​​​​y

Your email address will not be published. Required fields are marked

This site uses Akismet to reduce spam. Learn how your comment data is processed.

{"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}