Extending Default Spring Data Repository Methods (Patch Example)

Navid Ghahremani
3 min readJan 22, 2020

--

Coming back with another Spring trick written in Kotlin and Reactive Programming.

TL; DR.

Today, I will show you how to extend Spring Data repositories without implementing it every time in our repository interface.

If you do not want the details, here is the gist:

https://gist.github.com/ghahramani/c3c1f99097ceabb766a0060a6f96ab64

Disclosure:

  • I wrote all codes in Kotlin, but it would be understandable to convert it to Java. In case you need help, please let me know in the comments, so I will help you with that.
  • As I love MongoDB, I used Spring data MongoDB library, but the concept is the same for JPA and other databases, just base classes are different. Here is a link related to Spring data MongoDB https://spring.io/projects/spring-data-mongodb
  • The code is running under Spring WebFlux, so in that case; I am using reactive programming classes, but the concept is like non-reactive programming projects. For instance, you can use Collection instead of Flux or MongoRepository instead of ReactiveMongoRepository .
  • In the last one, the code is incomplete; it does not represent the model Book alongside other parts of the project as they are not our concerns for the article. Again, if you want a complete sample project, please let me know in the comments and I will provide it as soon as possible.

Let’s start:

Spring Data is a high-level database interaction library that uses ORM behind the scene and works with method name convention. For instance, if you want to search in your bookshelf with ignored case term and visible==true you can easily name your interface method like:

findByNameContainingIgnoreCaseAndVisibleIsTrue(term: String):Flux<Book>

Spring data works with the interface definition and the definition of its methods. It provides you some base interfaces to expand your repository functionalities like findById, deleteById, findAll and all other CRUD operations with a bunch of other powerful methods that you will not need to implement them at all, but sometimes they are not enough for you and you need to extend it by adding more methods, in that case, implement your method once and use it in all different repositories.

To use those already provided methods by Spring data, you need to extend your interface with ReactiveMongoRepository<DOMAIN, KEY_TYPE> like below:

Let’s not dive in details as it is not the focus of this article. For more information about Spring data, please check the links below (It is for MongoDB but there are a lot of similarities for JPA):

https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#repositories.query-methods.query-creationhttps://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#mongodb.repositories.queries

Now, implementation time.

How?

Let’s say we want to add a method in our base interface so we can use it for all of our repositories without implementing it every time for each repository. The base interface definition would be like:

Our base interface extends the Spring data base interface for the CRUD operations and other methods

Then you need a class to implement the base interface:

You need to extend SimpleReactiveMongoRepository as this is the class for Spring data implementation of its base interface

OK, we implemented our base interface. Now, we have to tell spring data to use our class whenever wants to implement our base interface. To do that, we should set repositoryBaseClass variable in theEnableReactiveMongoRepositories annotation like below:

Makes the Spring data be aware of our class for our interface implementation

OK, we are almost there, the last step is to use our base interface in our repository. Let’s see how we should do it.

Our repository extends our base interface

As you can see, our repository extends our base interface. Let’s review what is happening here. When we run the Spring application, it looks at the EnableReactiveMongoRepositories annotation variable named repositoryBaseClass then it implements our BaseMongoRepository methods with that class and finally; it implements our BookRepository method from its name/parameter and return type.

Conclusion:

So we learned how to extend our repositories and implemented their base methods once, use them in different repositories. This enables us to prevent duplication and spaghetti code. It also would be easier to test and maintain it. Generic can be handy here as it helps us to implement our method regardless of the entity type.

If you liked my article, please clap it and put some comments about your thoughts.

--

--