14 Implementing data persistence with Spring Data
Spring Data is presented as a way to simplify the persistence layer, which is the part of an application that stores, reads, updates, and deletes data in a database. Rather than writing full repository implementations by hand, developers define interfaces and let Spring generate the implementation at runtime. This reduces repetitive database code, keeps persistence logic easier to read and maintain, and helps teams focus on the business behavior of the application instead of boilerplate data access.
The chapter explains that Spring Data is not a single tool, but an umbrella project with separate modules for different persistence technologies such as JDBC, JPA, and MongoDB. These modules provide a common abstraction layer so repositories can be defined in a similar way even when the underlying technology differs. Core repository contracts like Repository, CrudRepository, and PagingAndSortingRepository let applications choose only the capabilities they need, while technology-specific interfaces such as JpaRepository or MongoRepository add operations suited to particular platforms.
The chapter then demonstrates Spring Data JDBC with a simple money-transfer application backed by an H2 database. An Account class is mapped to a table with the primary key marked by @Id, and an AccountRepository interface extends CrudRepository to gain standard CRUD behavior. For custom retrieval and update operations, methods can either rely on Spring Data’s naming conventions or, more clearly and efficiently, use @Query, with @Modifying required for data-changing statements. The repository is injected into a service that performs the transfer inside a transaction, and a REST controller exposes endpoints to move money and query account data, showing how Spring Data streamlines building database-driven features.
The Java ecosystem offers many various persistence technologies like Hibernate, JDBC, MongoDB, or others. Each technology has its own abstractions and class design. Spring Data offers a common abstraction layer over all these persistence technologies to simplify the use of multiple persistence technologies.
Using JDBC to connect to a relational DBMS is not the only choice for implementing an app’s persistence layer. In real-world scenarios, you’ll use other choices as well, and each way to persist data has its own library and set of APIs you need to learn to use. This variety adds a lot of complexity.
Sometimes apps use frameworks built on top of JDBC, such as Hibernate. The variety in choices makes implementing a persistence layer complex. We want to eliminate this complexity from our apps, and, as you’ll learn, Spring Data helps us do this.
Spring Data simplifies the persistence layer implementation by offering a common set of abstractions for various technologies.
If the app uses JDBC, it only needs the part of the Spring Data project that manages persistence through JDBC. The Spring Data module that manages the persistence through JDBC is called Spring Data JDBC. You add this Spring Data module to your app through its own dependency.
To implement your app’s repositories using Spring Data, you extend specific interfaces. The main interfaces that represent Spring Data contracts are Repository, CrudRepository, and PagingAndSortingRepository. You extend one of these contracts to implement your app’s persistence capabilities.
To create a Spring Data repository, you define an interface that extends one of the Spring Data contracts. For example, if your app only needs CRUD operations, the interface you define as a repository should extend the CrudRepository interface. The app adds a bean that implements the contract you define to the Spring context, so any other app components that need to use it can simply inject it from the context.
If the app needs sorting and paging capabilities, it should extend a more particular contract. The app provides a bean that implements the contract, which can then be injected from any other component that needs to use it.
Spring Data modules that are specific to certain technologies might provide particular contracts that define operations you can apply only with those technologies. When using such technologies, your app most likely will use these specific contracts.
Different Spring Data modules might provide other, more particular contracts. For example, if you use an ORM framework such as Hibernate (which implements the JPA) with Spring Data, you can extend the JpaRepository interface, which is a more particular contract that provides operations applicable only when using a JPA implementation, such as Hibernate.
The money transfer use case implies two steps. First, the app withdraws the transferred amount from the sender’s (John’s) account. Second, the app deposits the transferred amount into the receiver’s (Jane’s) account.
The relationship between the repository’s method name and the query Spring Data creates behind the scenes
Summary
- Spring Data helps implement a Spring application’s persistence layer by providing common abstractions over different persistence technologies.
- Repositories are usually defined as interfaces that extend Spring Data contracts such as:
- Repository, which defines no persistence operations
- CrudRepository, which provides basic CRUD operations
- PagingAndSortingRepository, which adds pagination and sorting support
- You choose the Spring Data module based on the persistence technology your application uses, such as Spring Data JDBC for JDBC-based access or Spring Data MongoDB for MongoDB.
- Repository interfaces inherit the operations defined by the contracts they extend, but they can also define custom operations.
- You can use @Query to explicitly define the query executed by a repository method.
- If no @Query is provided, Spring Data can derive the query from the method name. However, this requires following Spring Data naming rules, and invalid method names cause the application to fail at startup.
- Prefer @Query for complex operations because derived method names can become long, harder to maintain, slower to process at startup, and easier to break during refactoring.
- Operations that change data, such as INSERT, UPDATE, or DELETE, must be annotated with @Modifying.
Spring Start Here, Second Edition ebook for free