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

Kotlin Interface projections nullabillity issues #3242

Open
iPave opened this issue Feb 11, 2025 · 5 comments · May be fixed by #3244
Open

Kotlin Interface projections nullabillity issues #3242

iPave opened this issue Feb 11, 2025 · 5 comments · May be fixed by #3244
Assignees
Labels
in: kotlin Kotlin support type: bug A general bug

Comments

@iPave
Copy link

iPave commented Feb 11, 2025

Hi, I'm writing an interface projection and instead of getting some readable error while initializing the result I'm getting a runtime NullPointerException on non-nullable type.

@Entity
class Customer(
    @Id
    val id: Long? = null,

    var name: String? = null,

    val age: Int
)

interface CustomerRepository : CrudRepository<Customer, Int> {


    @Query("select c.name from Customer c")
    fun customProjectionQuery(): List<NameWithAgeOnly>

}

interface NameWithAgeOnly {
    val name: String
    val age: Int
}

@Service
class Service(
    private val customerRepository: CustomerRepository
) {

    @PostConstruct
    fun test() {
        val customerWithName = Customer(id = 1, name = "John", age = 25)
        val customerWithoutName = Customer(id = 2, name = null, age = 33)
        customerRepository.save(customerWithName)
        customerRepository.save(customerWithoutName)
        val customers = customerRepository.customProjectionQuery()
        for (customer in customers) {
            println(customer.name)
        }
    }
}

Caused by: java.lang.NullPointerException: Cannot invoke "org.dev.NameWithAgeOnly.getName()" because "customer" is null

Can you please clarify what am I doing wrong. And maybe give some workaround. Isn't it a bug?
I've also tried to use kotlin dto class instead of interface, but then I'm getting

Caused by: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap] to type [org.dev.NameWithAgeOnly]

Here is reproducible example: https://github.com/iPave/jpa-projections-issue/tree/main

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Feb 11, 2025
@mp911de mp911de self-assigned this Feb 12, 2025
@quaff
Copy link
Contributor

quaff commented Feb 13, 2025

It's caused by missing column aliases from tuple metadata, not sure Spring Data JPA could handle it, you can fix it by adding alias in your query like c.name as name.

IIRC, data class will be supported since 4.0.

BTW, you forgot c.age in your query.

@mp911de mp911de assigned christophstrobl and unassigned mp911de Feb 13, 2025
@iPave
Copy link
Author

iPave commented Feb 13, 2025

It's caused by missing column aliases from tuple metadata, not sure Spring Data JPA could handle it, you can fix it by adding alias in your query like c.name as name.

IIRC, data class will be supported since 4.0.

BTW, you forgot c.age in your query.

@quaff Thanks for you reply, even if I add aliases jpa just puts null to notNullable property and this code

    fun test() {
        val customerWithName = Customer(id = 1, name = "John", age = 25)
        val customerWithoutName = Customer(id = 2, name = null, age = 33)
        customerRepository.save(customerWithName)
        customerRepository.save(customerWithoutName)
        val customers = customerRepository.customProjectionQuery()
        for (customer in customers) {
            println(customer.name + " " + customer.age)
        }
    }

just prints:

John 25
null 33

But it should in my opinion give some error and not initialize not nullable type with null. I've made change and pushed to the repo fix of alias, so you can reproduce it. Thanks.

@quaff
Copy link
Contributor

quaff commented Feb 14, 2025

But it should in my opinion give some error and not initialize not nullable type with null.

I'm trying to fix it by #3244.

quaff added a commit to quaff/spring-data-commons that referenced this issue Feb 14, 2025
@christophstrobl
Copy link
Member

christophstrobl commented Feb 14, 2025

Thank you @iPave for reaching out.
The projecting proxy should react to the nullability constraints. Thanks @quaff for already submitting a PR.
We'll take care of it.

Regarding the DTO projection error, that will see an update with the data-jpa:3.5.0 release.

@christophstrobl christophstrobl added type: bug A general bug in: kotlin Kotlin support and removed status: waiting-for-triage An issue we've not yet triaged labels Feb 14, 2025
@iPave
Copy link
Author

iPave commented Feb 14, 2025

@quaff @christophstrobl thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: kotlin Kotlin support type: bug A general bug
Projects
None yet
5 participants