Using Kotlin and Spring 5 for some simple project.
I would like to get single record from database by id using queryForObject
.
My query is a 'simple select by id':
jdbc.queryForObject("select id, name from example where id = ?", id)
{ rs: ResultSet, _: Int -> NamedEnt(rs.getLong("id"), rs.getString("name") }
In JdbcOperationsExtensions.kt
it is declared to return nullable type T?
:
fun <T : Any> JdbcOperations.queryForObject(sql: String, vararg args: Any, function: (ResultSet, Int) -> T): T? =
queryForObject(sql, RowMapper { resultSet, i -> function(resultSet, i) }, *args)
In practice when I pass not existing identifier, I face with:
org.springframework.dao.EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0
Then I do not understand what is the point of returning nullable type? You either receive a single record or exception. Or I miss something?
JdbcOperationsExtensions.kt
adds some extension functions to the org.springframework.jdbc.core.JdbcOperations
interface (written in Java). If you look at the JavaDocs for queryForObject
in that, it says:
@return the single mapped object (may be {@code null} if the given
{@link RowMapper} returned {@code} null)
See here for full source code of the JdbcOperations
Java class.
So the Kotlin-written extension functions need to adhere to this and allow nulls to be returned, hence the nullable type.
Except... as pointed out by @AlexeyRomanov, this particular overload of queryForObject
takes in a lambda which returns T
, so can't ever return null, so arguably this overload could return T
, not T?
. Maybe it's a bit inconsistent that this lambda in Kotlin can't return null, but the JavaDocs on the very similar overload in the Java class explicitly state that it (RowMapper
) should be allowed to return null.
Regardless of that point, some other overloads of queryForObject
simply call to down to the Java-written overload, and because it's written in Java, it's possible that it could return a null. So for them it does seem to make sense for it to be a nullable return value. In which case arguably it's a nice bit of consistency that all the overloads do in fact return the nullable T
.