1

I have searched this in multiple places but I haven't found or understood the best approach this issue.

I have a query in Jooq with a left join, which results in rows being returned but with null values.

When it attempts to map it, it throws the following error:

java.lang.NullPointerException: null cannot be cast to non-null type kotlin.String

One way to fix this would be to make the generated code by jooq(getters) as nullable

Another approach will be to surround them by try/catch clause but i don't like it as I might have to do in multiple places.

Is there a cleaner approach? (apologies for any error in the code snippets as they have been changed to make it simpler)

  fun findById(id: String): User? {
        return dslContext.select()
            .from(User).leftJoin(USER_NAME).on(USER_NAME.ID.eq(USER.ID))
            .where(USER.ID.eq(id))
            .fetchGroups(USER, USER_NAME)
            .map { mapUserAndUserName(it) }
            .firstOrNull()
    }

  private fun mapUserAndUserName(mapItem: Map.Entry<UserRecord, Result<UsernameRecords>>): User {
        val record = mapItem.key
        val usernames = mapItem.value.mapNotNull { it?.username } // this is where i get the error 

        return mapUser(record, tags)
    }

Code generated by Jooq

open var tag: String
        set(value): Unit = set(1, value)
        get(): String = get(1) as String
2
  • Sometimes databases return null values and you can capture them with kotlin. Why choose a different way when such an option is available? I think this question should be answered first. Commented Feb 10 at 19:21
  • can you please elaborate on that, i'm new to this hence not sure what you mean Commented Feb 10 at 21:39

1 Answer 1

1

Background

The KotlinGenerator has a few flags to help users pretend they will be able to manage SQL nullability using Kotlin's nullability. The default settings for nullability are false, because this is such a footgun as mentioned throughout the manual, github issue tracker, and mailing list. E.g. in the above manual section:

While it is tempting to use non-nullability guarantees that are derived from your table definitions in generated code, it is important to remember that in SQL, there are numerous reasons why an expression that is based on a column declared as SQL NOT NULL can become NULL through the use of SQL operators, such as LEFT JOIN or UNION, etc. In particular, a column with a DEFAULT or IDENTITY expression is "nullable on write", so jOOQ will generate it as nullable despite a NOT NULL constraint!

Here's a popular issue that explains this impedance mismatch between SQL and kotlin more in depth:

Note that this topic even helped convince kotlin language designers that denotable nullability-flexible platform types (T!) are at least worth considering:

If you're using the JavaGenerator, then you won't have this issue because all of the generated Java values will be "platform types" which are at least null capable. If you're using the KotlinGenerator, you can pick your poison:

  • Default, which is more correct, but annoying: Everything is T?
  • Nullability-aware, which produces false positives: Non-nullable types are T, but they will cause trouble with a ton of SQL expressions

Regarding your specific question

You can simply add a check to see if the left joined record's primary key value is null, and then avoid the mapping. This will have to be done manually on your end, but at least like this, you can work around this specific problem.

What I mean is that instead of using the generated Record getter which makes a false promise in this case (as per your code generation configuration), just access the value using ordinary jOOQ API:

it[USER_NAME.USERNAME]

That will again give you a platform type T!, which you can null check.

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

Comments

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.