43

I'm writing a Spring Boot app with Spring Data JPA and Kotlin, and I've noticed that in CrudRepository there is the following method:

Optional<T> findById(ID id);

I'm using Kotlin, though, which has much more fluent ways of dealing with nulls than Optional. Does anyone know how I would convert that method to work like this?

fun findById(id: ID): T?

When I extend Repository itself and create a repo with that signature I get the error:

java.lang.ClassCastException: java.util.Optional cannot be cast to com.books.Book

3 Answers 3

78

As of Spring Data Lovelace SR4 / Spring Boot 2.1.2, a CrudRepository.findByIdOrNull(id: ID): T? = findById(id).orElse(null) Kotlin extension now provides out of the box a way to retrieve nullable entities in Spring Data.

If for performance reasons you would like to avoid the usage of Optional<T> wrapper, be aware that you have also the possibility to create a custom interface with a findFooById(id: ID): T? function. Query execution is store specific, but and most are using internally nullable values and will avoid the cost of Optional<T> wrapper. Notice this overhead should be negligible for most use cases, so using the builtin extension is recommended method.

See DATACMNS-1346 for more details.

Sign up to request clarification or add additional context in comments.

4 Comments

How can I use this kotlin extension? Do I need to do anything special? I get unresolvedReference when trying to use I from a repo that extends CrudRepository
Nevermind, had to import it from import org.springframework.data.repository.findByIdOrNull
However defining methods like findById(id: Id): T? in your own base interface that extends org.springframework.data.repository.Repository doesn't work. You'll get "class java.util.Optional cannot be cast to class ..." errors. You have to define methods for each specific type, e.g. findById(id: ThingId): Thing? instead of the generic variant above.
@deamon you can define a generic one you just can't use findById as that one is hardcoded. findKById worked for me
20

Update 12/2018:

An upcoming change in the Spring Data framework will make this answer obsolete. The update basically does the same as this answer: define an appropriate extension function. Please see Sébastien Deleuze's answer for further details.

Original answer:

As you correctly stated, you don't need Optional in Kotlin, because handling nullability in a concise manner is a build in language feature.

You could create your own extension function to achieve the desired behaviour:

fun <T, ID> CrudRepository<T, ID>.findOne(id: ID): T? = findById(id).orElse(null)

and use it like this:

val fruit: Fruit? = fruitRepository.findOne(id)

Thanks to Giordano who showed me a way to make the function more concise.

Comments

7

Short version of Sébastien Deleuze's answer: Just define a function with a nullable return type:

interface UserRepository : Repository<User, String> {

  // throws EmptyResultDataAccessException, if no user is found
  fun findByUsername(username: String): User     

  // return null, if no user is found
  fun findByFirstname(firstname: String?): User? 
}

See Spring Data Reference Documentation.

1 Comment

A VERY important thing not to forget here is to make the ARGUMENT nullable also (like it is also in the example in the reference). Didn't do it and sat a good few hours trouble-shooting. It must have to do with that a String in Java can be null (and thus corresponds to a nullable String? in Kotlin) whereas a long in Java is primitive and can never be null so it maps well with Long in Kotlin... so it's all about helping the compiler map signatures!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.