3

I'm new to Kotlin, and experimenting with spring-data-mongodb. Please see example below (also available here as fully runnable Maven project with in-memory MongoDb: https://github.com/danielsindahl/spring-boot-kotlin-example).

Application.kt

package dsitest

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication

@SpringBootApplication
open class Application

fun main(args: Array<String>) {
    SpringApplication.run(Application::class.java, *args)
}

User.kt

package dsitest

import org.springframework.data.annotation.Id
import org.springframework.data.annotation.PersistenceConstructor
import org.springframework.data.mongodb.core.mapping.Document

@Document(collection = "user")
data class User @PersistenceConstructor constructor(@Id val id: String? = null, val userName: String)

UserRepository.kt

package dsitest

import org.springframework.data.repository.CrudRepository

interface UserRepository : CrudRepository<User, String>

KotlinIntegrationTest.kt

package dsitest

import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.junit4.SpringRunner

@RunWith(SpringRunner::class)
@SpringBootTest
class KotlinIntegrationTest constructor () {

    @Autowired
    lateinit var userRepository : UserRepository;

    @Test
    fun persistenceTest() {
        val user : User = User(userName = "Mary")
        val savedUser = userRepository.save(user)
        val loadedUser = userRepository.findOne(savedUser.id) // Failing code
        println("loadedUser = ${loadedUser}")
    }
}

When running the test KotlinIntegrationTest.persistenceTest, I get the following error message when trying to retrieve a User object from MongoDb:

org.springframework.data.mapping.model.MappingException: No property null found on entity class dsitest.User to bind constructor parameter to!

If I modify the User data class so that userName is nullable, everything works.

data class User @PersistenceConstructor constructor(@Id val id: String? = null,
val userName: String? = null)

I would like to understand why this is the case, since I don't want userName to be nullable. Is there some alternative, better way of defining my User class in Kotlin?

Many thanks, Daniel

1 Answer 1

5

Yes, it is a known problem. You should check how the bytecode for your User class looks like. Java sees the constructor with all the parameters present and tries to call it with a null value for the 2nd one.

What you could do is to try adding @JvmOverloads to your constructor - this will force Kotlin compiler to generate all versions of the constructor and so the Spring Data Mongo could pick the correct one (get rid of the @PersistenceConstructor) then.

You could also define 1 constructor with no defaults - only for Java-based frameworks and 2nd one with some defaults your you. Or...

When I write things like you are now, I create simple 'persistence' data classes with no default values whatsoever that are mapped to/from my regular domain objects (a sort of abstraction over database). It may generate some overhead at the start - but keeping your domain model not coupled so tightly with the storage model is usually a good idea anyway.

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

3 Comments

Thanks Rafal, much appreciated. Not sure I fully understand though. Why does Spring invoke the instructor with a null value for userName? Why doesn't it use the userName it loaded from the database?
Alright (and LOL) I may be wrong there... Try adding @Field to second argument :)
This should be the accepted answer! (@JvmOverloads)

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.