kotlin-jdsl icon indicating copy to clipboard operation
kotlin-jdsl copied to clipboard

Is it possible to support rendering query that contains entities with `value class` fields?

Open leviathan-n opened this issue 1 year ago • 6 comments

spring data add support for kotlin value class at version 3.2 , so we can define an entity like this to avoid primitive obsession:


import jakarta.persistence.*

@Entity
class Brand(
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    val id: BrandId = BrandId(0),

    val name: String,
) {

    @Version
    val version: Int = 0
}

@JvmInline
value class BrandId(private val value: Long) : Comparable<BrandId> {  // jdsl Expressionable.gt requires type implements Comparable

    override fun compareTo(other: BrandId): Int =
        value.compareTo(other.value)
}

@Entity
class Product(

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    val id: ProductId = ProductId(0),

    val name: String,

    @Enumerated(EnumType.STRING)
    val type: ProductType,

    val brandId: BrandId,
) {

    @Version
    val version: Int = 0
}

@JvmInline
value class ProductId(val value: Long)

enum class ProductType {
    DRINK,
    TOOL,
    FOOD
}

and saying that there is a simple query

data class ProductWithBrand(
    val productId: ProductId,
    val productName: String,
    val brandId: BrandId,
    val brandName: String,
)

@Service
class TestService(
    private val jpqlRenderContext: JpqlRenderContext,
    private val entityManager: EntityManager,
) {

    fun test(): List<ProductWithBrand> {
        val query = jpql {
            select<ProductWithBrand>(
                path(Product::id),
                path(Product::name),
                path(Brand::id),
                path(Brand::name),
            )
                .from(
                    entity(Product::class),
                    join(Brand::class).on(path(Product::brandId).eq(path(Brand::id)))
                )
                .where(
                    entity(Product::class)(Product::name).like("coca")
                        .or(
                            path(Brand::name).eq("a")
                                .and(path(Brand::id).gt(BrandId(1L)))
                        )
                )
                .orderBy(path(Product::id).desc())
        }

        return entityManager.createQuery(query, jpqlRenderContext).apply { maxResults = 1 }.resultList
    }
}

and got an exception when executing it

org.hibernate.type.descriptor.java.CoercionException: Cannot coerce value 'BrandId(value=1)' [com.example.springpg.entity.BrandId] to Long
	at org.hibernate.type.descriptor.java.LongJavaType.coerce(LongJavaType.java:157) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
	at org.hibernate.type.descriptor.java.LongJavaType.coerce(LongJavaType.java:24) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
	at org.hibernate.query.internal.QueryParameterBindingImpl.coerce(QueryParameterBindingImpl.java:164) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
	at org.hibernate.query.internal.QueryParameterBindingImpl.setBindValue(QueryParameterBindingImpl.java:113) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
	at org.hibernate.query.spi.AbstractCommonQueryContract.setParameter(AbstractCommonQueryContract.java:835) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
	at org.hibernate.query.spi.AbstractSelectionQuery.setParameter(AbstractSelectionQuery.java:900) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
	at org.hibernate.query.sqm.internal.QuerySqmImpl.setParameter(QuerySqmImpl.java:1258) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
	at org.hibernate.query.sqm.internal.QuerySqmImpl.setParameter(QuerySqmImpl.java:140) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
	at com.linecorp.kotlinjdsl.support.spring.data.jpa.JpqlEntityManagerUtils.setParams(JpqlEntityManagerUtils.kt:220) ~[spring-data-jpa-support-3.2.0.jar:na]
	at com.linecorp.kotlinjdsl.support.spring.data.jpa.JpqlEntityManagerUtils.createQuery(JpqlEntityManagerUtils.kt:92) ~[spring-data-jpa-support-3.2.0.jar:na]
	at com.linecorp.kotlinjdsl.support.spring.data.jpa.JpqlEntityManagerUtils.createQuery(JpqlEntityManagerUtils.kt:30) ~[spring-data-jpa-support-3.2.0.jar:na]
	at com.linecorp.kotlinjdsl.support.spring.data.jpa.extension.EntityManagerExtensionsKt.createQuery(EntityManagerExtensions.kt:20) ~[spring-data-jpa-support-3.2.0.jar:na]
	at com.example.springpg.service.TestService.test(TestService.kt:41) ~[main/:na]
	at com.example.springpg.SpringPgApplication$route$1$1$1.invoke(SpringPgApplication.kt:25) ~[main/:na]
	at com.example.springpg.SpringPgApplication$route$1$1$1.invoke(SpringPgApplication.kt:24) ~[main/:na]
	at org.springframework.web.servlet.function.RouterFunctionDsl.GET$lambda$0(RouterFunctionDsl.kt:153) ~[spring-webmvc-6.1.2.jar:6.1.2]
	at org.springframework.web.servlet.function.support.HandlerFunctionAdapter.handle(HandlerFunctionAdapter.java:107) ~[spring-webmvc-6.1.2.jar:6.1.2]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) ~[spring-webmvc-6.1.2.jar:6.1.2]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) ~[spring-webmvc-6.1.2.jar:6.1.2]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[spring-webmvc-6.1.2.jar:6.1.2]
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) ~[spring-webmvc-6.1.2.jar:6.1.2]
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) ~[tomcat-embed-core-10.1.17.jar:6.0]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) ~[spring-webmvc-6.1.2.jar:6.1.2]
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) ~[tomcat-embed-core-10.1.17.jar:6.0]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205) ~[tomcat-embed-core-10.1.17.jar:10.1.17]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.17.jar:10.1.17]
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[tomcat-embed-websocket-10.1.17.jar:10.1.17]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.17.jar:10.1.17]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.17.jar:10.1.17]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.1.2.jar:6.1.2]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.2.jar:6.1.2]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.17.jar:10.1.17]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.17.jar:10.1.17]
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-6.1.2.jar:6.1.2]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.2.jar:6.1.2]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.17.jar:10.1.17]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.17.jar:10.1.17]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-6.1.2.jar:6.1.2]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.2.jar:6.1.2]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.17.jar:10.1.17]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.17.jar:10.1.17]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[tomcat-embed-core-10.1.17.jar:10.1.17]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) ~[tomcat-embed-core-10.1.17.jar:10.1.17]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482) ~[tomcat-embed-core-10.1.17.jar:10.1.17]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115) ~[tomcat-embed-core-10.1.17.jar:10.1.17]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) ~[tomcat-embed-core-10.1.17.jar:10.1.17]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-10.1.17.jar:10.1.17]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:340) ~[tomcat-embed-core-10.1.17.jar:10.1.17]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391) ~[tomcat-embed-core-10.1.17.jar:10.1.17]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) ~[tomcat-embed-core-10.1.17.jar:10.1.17]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896) ~[tomcat-embed-core-10.1.17.jar:10.1.17]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1744) ~[tomcat-embed-core-10.1.17.jar:10.1.17]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[tomcat-embed-core-10.1.17.jar:10.1.17]
	at java.base/java.lang.VirtualThread.run(VirtualThread.java:309) ~[na:na]

leviathan-n avatar Jan 01 '24 14:01 leviathan-n