Your own custom Spring Data repository

Frameworks promise to speed up one’s development pace provided one follows the mainstream path. The path may be more or less narrow. I’m a big fan of the Spring ecosystem because its design is extensible and customizable at different abstraction levels: thus, the path is as large as you need it to be.

Functional Programming is becoming more and more popular. Spring provides a couple of DSLs for the Kotlin language. For example, the Beans DSL and the Routes DSL allow for a more functional approach toward Spring configuration. On the type side, Vavr (previously Javaslang) is pretty popular in Java, while Kotlin has Arrow.

In this post, I’d like to describe how one can use Arrow’s type system with Spring Data. Ultimately, you can benefit from the explanations to craft your custom Spring Data repository.

The starting architecture

The starting architecture for my application is pretty standard:

  1. A REST controller with two mappings
  2. A Spring Data JDBC repository interface

Because it’s standard, Spring handles a lot of the plumbing, and we don’t need to write a lot of code. With Kotlin, it’s even more concise:

Toward a more functional approach

This step has nothing to do with Spring Data and is not required, but it fits the functional approach better. As mentioned above, we can benefit from using the Routes and Beans DSL. Let’s refactor the above code to remove annotations as much as possible.

  1. Create a handler class to organize the routing functions
  2. All routing functions should accept a parameter and return a
  3. Add an additional capability: if the entity is not found, return a 404
  4. Use the Routes DSL to map HTTP verbs and path to routing functions
  5. retrieves bean with the configured type from Spring’s application context
  6. Explicitly call the function, no more magic!

All routing functions should accept a parameter and return a

Introducing Arrow

Functional companion to Kotlin’s Standard Library


Arrow comes with four different components:

  1. Core
  2. FX: Functional Effects Framework companion to KotlinX Coroutines
  3. Optics: Deep access and transformations over immutable data
  4. Meta: Metaprogramming library for Kotlin compiler plugins

The Core library offers the type. Arrow advises using to model an optional value. On the other side, Spring Data JDBC returns a .

Bridging the gap

How do we bridge the gap between and ?

Here’s a first attempt:

  1. At this point, we can safely call to get a

I believe the usage of is not great. However, Kotlin can help us in this regard with extension functions:

With this function, we can improve the existing code:

It looks nicer this way, but it would be so much better to have the repository return an directly.

Spring Data customization

Let’s check how we can customize Spring Data to achieve that.

By default, a Spring Data repository offers all generic functions you can expect, .e.g.:

I believe that one comes to Spring Data for ease of use, but that one stays for its extensibility capabilities.

At the base level, one can add functions that follow a certain naming pattern, e.g., . Spring Data will generate the implementing code without you needing to write a single line of SQL. When you hit the limits of this approach, you can annotate the function with the SQL that you want to run.

In both cases, you need to set the return type. While the number of possible return types is pretty huge, it’s still limited. The framework cannot account for every possible type, and specifically, the list doesn’t contain .

The next extensibility level is to add any function with the desired signature via a custom implementation. For that, we need:

  • An interface that declares the wanted function
  • A class that implements the interface
  1. New custom interface
  2. Declare the wanted function
  3. New implementing class…​
  4. …​ that implements the parent interface
  5. Implement the function
  6. Just extend the custom interface

Now, we can call:

This approach works but has one major flaw. To avoid a clash in the functions’ signature, we have to invent an original name for our function that returns i.e. .

Changing the default base repository

To overcome this limitation, we can leverage another extension point: change the default base repository.

Spring Data applications define interfaces, but the implementation needs to come from somewhere. The framework provides one by default, but it’s possible to switch it with our own.

Here’s an overview of the class diagram:

The detailed flow is pretty complex: the important part is the class. Spring Data will find the class via the bean, create a new instance of it and register the instance in the context.

Let’s create a base repository that uses :

  1. Our new interface repository…​
  2. …​with the signature we choose without any collision risk.
  3. I was too lazy to implement everything.
  4. The base implementation for the repository interface.
  5. The constructor needs to accept those two parameters.
  6. Don’t reinvent the wheel; use the existing instance.

We need to annotate the main application class with and configure the latter to switch to this base class.

To ease the usage from the client code, we can create an annotation that overrides the default value:

Now, the usage is straightforward:

At this point, we can move the Arrow repository code into its project and distribute it for other “client” projects to use. No further extension is necessary, though Spring Data offers much more, e.g., switching the factory bean.


Spring Data provides a ready-to-use repository implementation out-of-the-box. When it’s not enough, its flexible design makes it possible to extend the code at different abstraction levels.

This post showed how to replace the default base repository with our own, which uses an Arrow type in the function signature.

Thanks to Mark Paluch for his review.

The complete source code for this post can be found on Github in Maven format.

Originally published at A Java Geek on April 11th, 2021.

Dev Advocate @Hazelcast . Former developer and architect. Still teaching, learning and blogging.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store