1

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?

Kirill
  • 6,762
  • 4
  • 51
  • 81

1 Answers1

2

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.

Yoni Gibbs
  • 6,518
  • 2
  • 24
  • 37
  • "So the Kotlin-written extension functions need to adhere to this and allow nulls to be returned" Except they don't: `function: (ResultSet, Int) -> T` can't return `null`, so the `RowMapper` using it can't return `null`. – Alexey Romanov Mar 25 '19 at 17:26
  • Ha! Well spotted @AlexeyRomanov: I missed that. I wonder if this is a small bug in that extension method then: maybe the lambda passed into `queryForObject` should be declared as having a return type of `T?`. Because the JavaDocs seem to suggest that the `RowMapper` should be allowed to return null. – Yoni Gibbs Mar 25 '19 at 19:41
  • Agreed. Issue logged [here](https://github.com/spring-projects/spring-framework/issues/22682). – Yoni Gibbs Mar 26 '19 at 10:27
  • @AlexeyRomanov Could you elaborate on why these extensions should be `inline`? Performance? – Sébastien Deleuze Mar 26 '19 at 13:42
  • Yes. The win is not that large, especially when comparing to database access, but in my opinion you need a reason _not_ to declare such methods inline (e.g. https://kotlinlang.org/docs/reference/inline-functions.html#public-inline-restrictions). Maybe there is such a reason for this case, I just don't see it. – Alexey Romanov Mar 26 '19 at 13:59
  • If we do that I guess that should apply to any Kotlin extension that invoke it's java counterpart, could you please create an issue for that on https://github.com/spring-projects/spring-framework/issues ? – Sébastien Deleuze Mar 26 '19 at 14:40