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

Java generics are incorrectly generated in Kotlin (Generated an "Set<? extends Feature>") #3287

Open
TheSmileCat opened this issue Dec 7, 2023 · 3 comments

Comments

@TheSmileCat
Copy link

TheSmileCat commented Dec 7, 2023

Environment

Java 17, Kotlin 1.9.20, Gradle: 8.2.1 and this configuration.
Tried to update to "13.25.1", but the issue is still unresolved

plugins {
    id("io.ebean") version "13.6.5"
    ...
}
dependencies {
    implementation("io.ebean:ebean:13.22.0") {
        exclude("io.ebean", "ebean-platform-all")
    }
    implementation("io.ebean:ebean-platform-sqlite:13.22.0")
    implementation("org.xerial:sqlite-jdbc:3.43.2.1")
    kapt("io.ebean:kotlin-querybean-generator:13.22.0")
    testImplementation("io.ebean:ebean-test:13.22.0")
}

Expected behavior

Generate an lateinit var feature: PScalar<QDGameProdEntity, Set<Feature>>

Actual behavior

Path: build/kaptKotlin/main/foo/bar/query/QDGameProdEntity.kt

// Strange "import" text
import ?;
import extends;
import foo.bar.Feature;
import foo.bar.QDGameProdEntity;
// Some things are omitted

lateinit var feature: PScalar<QDGameProdEntity, Set<? extends Feature>>

Why does the Java code <? extends Feature> appear here ?

Steps to reproduce

package foo.bar

import foo.bar.Feature
import io.ebean.*
import io.ebean.annotation.*
import java.util.*
import javax.persistence.*

@Entity
@DbName("decision")
@Table(name = "game_prod")
class DGameProdEntity : Model("decision") {
    @Id
    lateinit var id: UUID
    
    // The database stores this set using bitwise operations.
    // bits: 00000000 ~ 00011111
    @Column(name = "prop_feature")
    @Convert(converter = FeatureConverter::class)
    var feature: Set<Feature> = emptySet()

    class FeatureConverter : AttributeConverter<Set<Feature>, Int> {
        override fun convertToDatabaseColumn(attribute: Set<Feature>?): Int {
            if (attribute == null) return 0
            return Feature.entries.foldIndexed(0) { o, i, it ->
                if (it in attribute) o or (1 shl i) else o
            }
        }

        override fun convertToEntityAttribute(dbData: Int?): Set<Feature> {
            if (dbData == null) return mutableSetOf()
            val rs = mutableSetOf<Feature>()
            Feature.entries.forEachIndexed { i, it ->
                if (((dbData ushr i) and 1) == 1) rs.add(it)
            }
            return rs
        }
    }
}

enum class Feature{ A,B,C,D,E }
Task :compileKotlin FAILED
e: QDGameProdEntity.kt:3:8 Expecting qualified name
e: QDGameProdEntity.kt:58:55 Type expected
e: QDGameProdEntity.kt:58:56 Expecting a '>'
e: QDGameProdEntity.kt:58:57 Property getter or setter expected
e: QDGameProdEntity.kt:7:8 Unresolved reference: extends
@rbygrave
Copy link
Member

Where are you at with this issue? Did you workaround it? Can you manually produce a QueryBean with the code that you would expect to be generated for us?

@rbygrave
Copy link
Member

rbygrave commented Mar 3, 2024

Are you able to give an update on this? Did you remove the attribute converter?

This is an interesting one, ultimately we'd want to be able to know that query predicates for this are translating into bit wise database functions etc so I think handling this via a AttributeConverter is actually problematic in the end (because it isn't smart enough to translate the query predicts). I think we need a different approach here.

@TheSmileCat
Copy link
Author

TheSmileCat commented Mar 3, 2024

I used the kotlin extension property instead of the @Convert.
However, this means that the original database fields appear in the class, and the extended properties don't appear in the serialized object (e.g., json), which obviously doesn't seem very elegant, but for now, that's the best we can do.
Of course, each read or write to this extension property incurs an additional performance penalty, which annoy me. Maybe the performance loss is not big, and it's my mind fell into a hole.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants