Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect deserialization of a kotlin class when loading it from mongodb #4733

Closed
gasabr opened this issue Jun 25, 2024 · 3 comments
Closed
Assignees
Labels
status: feedback-provided Feedback has been provided status: waiting-for-triage An issue we've not yet triaged

Comments

@gasabr
Copy link

gasabr commented Jun 25, 2024

I'm sorry if this is the wrong place to file an issue, I've not found the right one in 10 minutes of googling.

Environment

  • spring: 3.0.4
  • kotlin 1.8.10
  • jvm 17
  • kotlin("plugin.spring") 1.8.10
  • mongo 6.0 local

Here is the class I'm trying to read from the database

@Document(collection = "users")
internal data class User(
    @Id
    val userId: UserId,
    val telegramChatId: Long,
)

here is the repository

@Repository
internal interface UserRepository : CrudRepository<User, String> {
    fun findByTelegramChatId(telegramChatId: Long): User?
}

Saving works just fine, but when I'm trying to load it from the database, I'm getting a weird error Parameter org.springframework.data.mapping.Parameter@a972a9cc does not have a name, inside of the org.springframework.data.mapping.model.ClassGeneratingEntityInstantiator#extractInvocationArguments. I've debugged the code and noticed that the last parameter in the extractInvocationArguments method is null and it identifies a default constructor. So, I added PersistenceCreator to my class like this

    companion object {
        @JvmStatic
        @PersistenceCreator
        fun create(
            userId: UserId,
            telegramChatId: Long,
        ) = User(userId, telegramChatId)
    }

and everything works correctly now. The root cause of the problem lies in spring data adding an argument to the default class constructor, PersistenceCreator fixes it, because it is being parsed by the spring correctly. If needed I can debug once again and point to the functions there I think resolution is incorrect. The UserId is a value class, which has dedicated converter, but it works as expected.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jun 25, 2024
@mp911de
Copy link
Member

mp911de commented Jun 25, 2024

Care to provide the UserId type? Ideally, you would provide a minimal reproducer as GitHub repo or zip file (attached to this issue) so that we have an exact reproducer.

One more thing: Looking at your dependency versions, it seems that you're using Spring Boot 3.0.4 that uses Spring Data Commons 3.0.4.

Kotlin value class support was introduced in a later version 3.2, as I'd anticipate inline/value class usage here.

@mp911de mp911de added the status: waiting-for-feedback We need additional information before we can continue label Jun 25, 2024
@gasabr
Copy link
Author

gasabr commented Jun 25, 2024

Providing minimal working example would take some time, because project is a bit messy rn, so here are the classes

internal class UserId(val id: String) {

    companion object {
        fun fromString(eventId: String?): UserId {
            if (eventId == null) {
                throw RuntimeException("UserId can not be null.")
            }
            return UserId(eventId)
        }

        fun createNew(): UserId = UserId("user-" + getRandomString(15))
    }
    // skipped toString, equals, hashCode, which I generated trying to find the root cause
}

Converters

@WritingConverter
internal class UserIdToStringConverter : Converter<UserId, String> {
    override fun convert(source: UserId): String {
        return source.id
    }
}

@ReadingConverter
internal class StringToUserIdConverter : Converter<String, UserId> {
    override fun convert(source: String): UserId {
        return UserId.fromString(source)
    }
}

Thanks for the ticket link, didn't know value classes were not supported yet, I'll pack the minimal reproducable example later today, but from the debugging I've done, it's not the "value" class, because UserId object is being restored correctly, the problem lies in parameters list having 3 values instead of 2, where first 2 are real parameters and the last one is null.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Jun 25, 2024
@christophstrobl christophstrobl transferred this issue from spring-projects/spring-data-jpa Jun 28, 2024
@gasabr
Copy link
Author

gasabr commented Jul 5, 2024

i think i messed up my dependencies somewhere, because I can not create a minimal reproducible example with current versions of spring.
Sorry, for bothering you, will reopen in case I make an repro:(

@gasabr gasabr closed this as completed Jul 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: feedback-provided Feedback has been provided status: waiting-for-triage An issue we've not yet triaged
Projects
None yet
Development

No branches or pull requests

4 participants