Spring Data Relational: the deliberately small ORM for JDBC and R2DBC
Spring Data Relational. Home of Spring Data JDBC and Spring Data R2DBC.
At a glance
- What is it?
- The home of Spring Data JDBC and Spring Data R2DBC, a repository framework for SQL databases that leaves out caching, lazy loading and write behind. It is opinionated on purpose, and that is the point.
- Who is it for?
- Spring Data Relational suits a Spring application that wants a familiar repository interface over JDBC or R2DBC while keeping its persistence model visible and its SQL explicit, especially a reactive service built on R2DBC where a lazy loading ORM would fight the reactive model. It does not suit a codebase that relies on the JPA feature set, since the omissions are deliberate and there is no compatibility layer for them.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly Java, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.
Editorial analysis
An ORM defined by what it leaves out
Spring Data Relational is part of the Spring Data family and makes it easy to implement repositories for SQL databases, with enhanced support for JDBC and R2DBC based data access layers. The design decision that defines the project is stated plainly in the README. It does not offer caching, lazy loading, write behind or many of the other features of JPA, and the result is that Spring Data JDBC and Spring Data R2DBC are a simple, limited, opinionated ORM. That is the pitch and the trade in one sentence. In exchange for giving up the session and the persistence context, you get a persistence model where the shape of your data is described by the aggregates you write, and where the SQL is close to the surface.
The feature list is short enough to quote in meaning rather than in full: CRUD method implementations for aggregates, a @Query annotation, transparent auditing for created and last changed timestamps, events for persistence events, the possibility to integrate custom repository code, JavaConfig based repository configuration through @EnableJdbcRepositories or @EnableR2dbcRepositories, and, on the JDBC side only, integration with MyBatis.
Aggregates as Java classes and repositories as interfaces
The application model is an aggregate, marked as a table, with an identifier field. The README's teaser is a Person class with an id and two name fields:
@Table
public class Person {
@Id
private Long id;
private String firstname;
private String lastname;
// getters and setters
}The repository is an interface extending CrudRepository, with the aggregate type and the identifier type as parameters:
interface PersonRepository extends CrudRepository<Person, Long> {
@Query("SELECT * FROM person WHERE lastname = :lastname")
List<Person> findByLastname(String lastname);
@Query("SELECT * FROM person WHERE firstname LIKE :firstname")
List<Person> findByFirstnameLike(String firstname);
}The service injects the interface and calls it. There is no entity manager and no unit of work in the code the user writes, which is the visible consequence of the omissions. For readers coming from JPA, the README points to two articles, Introducing Spring Data JDBC and Spring Data JDBC, References and Aggregates, and those are the pieces to read before the annotations, because the reference and aggregate model is where the thinking has to happen.
Querying with named parameters and derived method names
Two query styles appear in the same repository interface above. The explicit one uses @Query with a colon-prefixed named parameter, so the method argument is bound by name rather than by position. The second looks like a derived query, findByFirstnameLike, where the method name carries the filter, and the @Query annotation on it pins the SQL that backs it. The README's service example shows both in use side by side, calling findByLastname with an exact surname and findByFirstnameLike with a prefix pattern. For the reactive side the same query strings are reused, and the return types change, which is the main structural difference between the two halves of the project. A method that returns a List in JDBC returns a Flux in R2DBC, and the derived query style is available in both.
Two halves, one repository model
The reactive variant is not a port of the blocking one, it is the same repository model expressed in reactive types. The interface extends ReactiveCrudRepository and the queries return Flux, and the service chains them, deleting all and then concatenating the two result streams:
interface PersonRepository extends ReactiveCrudRepository<Person, Long> {
@Query("SELECT * FROM person WHERE lastname = :lastname")
Flux<Person> findByLastname(String lastname);
}Configuration mirrors that symmetry. The blocking application uses @EnableJdbcRepositories on a class extending AbstractJdbcConfiguration and supplies a DataSource together with a NamedParameterJdbcTemplate, while the reactive application uses @EnableR2dbcRepositories on a class extending AbstractR2dbcConfiguration and supplies a ConnectionFactory built from a connection factory URL. If you have to support both, the repository interfaces can look almost identical, and the module structure in the repository root mirrors it with separate spring-data-jdbc, spring-data-r2dbc and spring-data-relational directories, plus a distribution module.
Adding the dependency, and where the snapshots live
The Maven coordinates are the usual Spring Data ones, one artifact per flavour:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jdbc</artifactId>
<version>${version}</version>
</dependency>The R2DBC artifact is spring-data-r2dbc in the same group. If you want the latest snapshots of the upcoming major version, the README points to the Spring snapshot repository, with the dependency version suffixed with -SNAPSHOT and the repository declared as spring-snapshot, or spring-libs-snapshot in the R2DBC example, both pointing at https://repo.spring.io/snapshot. Both examples use a ${version} placeholder, so the project assumes you track a specific release line rather than a floating version, which is consistent with the way Spring Data modules are versioned alongside Spring Framework.
Version 4.2 milestones in flight, Apache 2.0 terms
The release state is a 4.x line with milestones in progress. Recent tags are 4.1.1 on 2026-08-21, then the 4.2.0-M1 milestone on the same day and 4.2.0-M2 on 2026-09-24, and the repository is not archived with the last push on 2026-10-15. So the stable line and the next milestone line are both moving, which is the normal cadence for a Spring project. The licence is Apache 2.0 with the licence file at the repository root, and the README asks upgrading users to check the changelog on the releases page for the new and noteworthy features. The root listing also shows the housekeeping that comes with a Spring project, with a Maven wrapper, a lombok.config, CI configuration and a SECURITY.adoc alongside the code.
Editorial conclusion
Spring Data Relational suits a Spring application that wants a familiar repository interface over JDBC or R2DBC while keeping its persistence model visible and its SQL explicit, especially a reactive service built on R2DBC where a lazy loading ORM would fight the reactive model. It does not suit a codebase that relies on the JPA feature set, since the omissions are deliberate and there is no compatibility layer for them. Before adopting, read the two introductory blog posts the README links, because the aggregate and reference model is the concept to understand before the annotations.
Frequently asked questions
What is Spring Data Relational?
It is the module of the Spring Data family that holds Spring Data JDBC and Spring Data R2DBC. It provides repository implementations for SQL databases over JDBC and R2DBC.
Does Spring Data JDBC support caching or lazy loading?
No. The README states it does not offer caching, lazy loading, write behind or many other JPA features, which is what makes Spring Data JDBC and Spring Data R2DBC a simple, limited, opinionated ORM.
How do I enable Spring Data JDBC repositories in a Spring application?
Annotate a configuration class with @EnableJdbcRepositories and extend AbstractJdbcConfiguration, providing a DataSource bean and a NamedParameterJdbcTemplate. The reactive equivalent is @EnableR2dbcRepositories on a class extending AbstractR2dbcConfiguration with a ConnectionFactory.
How do I write a custom query in Spring Data JDBC?
Use the @Query annotation with a colon-prefixed named parameter, for example @Query("SELECT * FROM person WHERE lastname = :lastname"), as shown on a method of a CrudRepository interface.
Which Maven artifact do I add for Spring Data R2DBC?
org.springframework.data:spring-data-r2dbc, with snapshots available from the Spring snapshot repository at https://repo.spring.io/snapshot using a -SNAPSHOT version suffix.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/spring-projects-spring-data-relational)Was this useful?