Coming Up for Air

Custom Methods in Spring Repositories

Friday, February 28, 2020 |

One of the great things about Spring Data Repositories is that they provide a number of query methods out of the box, with the ability to add additional queries simply by adding carefully named methods to the interface, and Spring generates the actual implementation for you. Sometimes, though, you do need to color outside the lines a bit. Thankfully, Spring allows us to do this. You just have to ask it nicely. Here’s now.

For the sake of space, I’m going to assume you know how to create a simple Spring Data repository, so I’ll leave out the details. That said, we’ll start with a basic repository:

1
interface FooRepository : CrudRepository<Foo, Long>

Let’s say, for demonstration purposes, that we need to add a method that requires a complex query that can’t be modeled, easily, cleanly, or otherwise, using the required semantics. You can, of course, specify the query using @Query rather than letting Spring derive it, but sometimes not even that’s enough. You have some really complex logic you need to implement to make the query work, so you need to write it explicitly, let’s say getSomeFoosBasedOnComplexLogic. Nice, huh?

Let’s start by defining an additional interface:

1
2
3
interface FooRepositoryCustom {
    fun getSomeFoosBasedOnComplexLogic(oaram1 Int, param2 String) : List<Foo>
}

Having defined the interface, we need to define its implementation:

1
2
3
4
5
6
class FooRepositoryCustomImpl(private val entityManager : EntityManager) : FooRepositoryCustom {
    fun getSomeFoosBasedOnComplexLogic(param1 Int, param2 String) : List<Foo> {
        // Do some JPA query here, but we'll return dummy data
        return listOf(Foo("bar"), Foo("baz"))
    }
}

Now we need to revisit FooRepository and update it:

1
interface FooRepository : CrudRepository<Foo, Long>, FooRepositoryCustom

When Spring creates the FooRepository instance, it will do all the magic required to wire in FooRepositoryCustomImpl as part of the resulting class. How I don’t know, to be honest (but I’m guessing it delegates from generated code), but it’s not too terribly important at this point. What is important is that we can call this custom method like any other. For example:

1
2
3
4
5
6
7
8
9
@CrossOrigin
@RestController
@RequestMapping("/dummy")
class DummyController(private val repo : FooRepository) {
    @GetMapping
    fun foo(): List<Foo> {
        return repo.getSomeFoosBasedOnComplexLogic()
    }
}

Hit that with your favorite client, and you should see, e.g., a nice JSON representation of a List<Foo>.

See? Super easy once you know how to hold your mouth just right!

Search

    Quotes

    Sample quote

    Quote source

    About

    My name is Jason Lee. I am a software developer living in the middle of Oklahoma. I’ve been a professional developer since 1997, using a variety of languages, including Java, Javascript, PHP, Python, Delphi, and even a bit of C#. I currently work for Red Hat on the WildFly/EAP team, where, among other things, I maintain integrations for some MicroProfile specs, OpenTelemetry, Micrometer, Jakarta Faces, and Bean Validation. (Full resume here. LinkedIn profile)

    I am the president of the Oklahoma City JUG, and an occasional speaker at the JUG and a variety of technical conferences.

    On the personal side, I’m active in my church, and enjoy bass guitar, running, fishing, and a variety of martial arts. I’m also married to a beautiful woman, and have two boys, who, thankfully, look like their mother.

    My Links

    Publications