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
The JPA specification defines that during ordering, NULL values shall be handled in the same way as determined by the SQL standard. The standard specifies that all null values shall be returned before or after all non-null values. It's up to the database to pick one of the two options.
Following example shows how to use IS NULL to find properties values which have not been set.
Depending on whom you ask, using Optional as the type of a field or return type is bad practice in Java. Fortunately, in Kotlin there is no arguing about it. Using nullable types in properties or as the return type of functions is considered perfectly valid and idiomatic in Kotlin.
The normal behavior is indeed returning an empty list if no results are found. If a List<Object> is the return value of the method in the defined interface, the method should never return Null . The problem is that a parameter is given to the method and is not used anywhere in the Query.
It provides you with smart completion for method names and quick-fixes for parameters: Also, custom queries with Expression Language variables are now supported: Please try using JPA and Spring Data with Kotlin and share your experience with us. More posts about Kotlin with Spring support in IntelliJ IDEA are coming.
The Kotlin compiler generates the default method implementations based on all properties of a data class. When we’re using data classes for JPA entities, some of those properties might be a lazy association with the target entity.
Since nullable types in Kotlin are not wrapped in another class like Optional, there’s no need for an equivalent of the get () method - just assign the value where you need it. Again, we have to go out of our way to throw an exception to match the Java API. To provide a default to use when the value is null, use the safe call operator.
When mapping an Optional in Java, sometimes you have to unwrap another Optional. To do this, you use flatMap () instead of map (). With Kotlin’s null system, the value is either present, or null, so there’s nothing to unwrap.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With