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
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.
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