line / kotlin-jdsl

Kotlin library that makes it easy to build and execute queries without generated metamodel
https://kotlin-jdsl.gitbook.io/docs/
Apache License 2.0
651 stars 85 forks source link

JpqlFunctionSerializerSupport, Function Contributor doesn’t work on Hibernate 6.5 #726

Closed cj848 closed 2 weeks ago

cj848 commented 2 weeks ago

https://discourse.hibernate.org/t/function-contributor-doesnt-work-on-hibernate-6-5/9717

Referring to the link above, the actual jpql function serializer does not work after upgrading hibernate 6.5.x version. As shown in the link attached above, JPQL must be generated using a method other than the existing method.

However, I have not checked whether this only applies to 6.5.x or whether the same expression is possible in versions lower than 6.5. (However, it is expected that it will not operate normally)

In this case, backward compatibility can be seen as broken. I need your opinion on how best to respond @shown

--- korean https://discourse.hibernate.org/t/function-contributor-doesnt-work-on-hibernate-6-5/9717

위 링크를 참고하면 hibernate 6.5.x 버전을 업그레이드한 후 실제 jpql function serializer 가 동작하지 않습니다. 위에 첨부한 링크에 있는 것처럼 기존 방식이 아닌 다른방식으로 JPQL을 생성해야 합니다.

다만 이 부분이 6.5.x 에만 적용되는건지 6.5 보다 아래 버전에서도 동일한 표현식으로 가능한지 확인해보지는 않았습니다. (다만 정상동작하지 않을 것으로 예상됩니다)

이경우 하위 호환이 깨진 것으로 볼 수 있는데 대응을 어떻게 하는게 좋을지 의견이 필요합니다 @shown

shouwn commented 2 weeks ago

JPA provides the function keyword as an expression. https://jakarta.ee/specifications/persistence/3.1/jakarta-persistence-spec-3.1.html#a5311

This is an internal implementation issue in Hibernate, so it is not considered backwards compatible with the Kotlin JDSL. Therefore, it is better to not modify the code in the Kotlin JDSL and guide users using Hibernate to use customExpression if they want to use function.

--- korean JPA는 function 키워드를 표현식으로 제공하고 있습니다.

위 이슈는 Hibernate 내부 구현의 이슈이기 때문에, Kotlin JDSL가 하위 호환성을 고민해야 할 문제는 아니라고 생각합니다. 따라서 Kotlin JDSL이 코드를 수정하기 보다 Hibernate를 사용하는 유저에게 customExpression을 사용하도록 가이드하는 것이 더 좋다고 생각합니다.

cj848 commented 2 weeks ago

you're right. This is something I raised as an issue because I agree with it. It appears that it is difficult to solve this problem accurately at the JDSL level.

The only way to handle this part is to guide the user in the form of creating a customExpression, and based on the current specifications, I understand that the implementation of JDSL is correct.

Then, I think it would be a good idea to close the issue and our future response would be to add a link to this issue. --- korean 맞습니다. 저도 동의하기 때문에 이슈로 제기했던 부분입니다. 이에 대해서 해결을 JDSL 레벨에서 정확히 하기는 어려움이 있는것으로 보입니다.

이부분에 대한 처리는 사용자에게 customExpression 을 만드는 형태의 가이드로 나가는 수밖에 없을것이고 현재 스펙 기준으로는 JDSL의 구현이 맞는것으로 이해하고 있겠습니다.

그러면 이슈를 닫고 이후의 우리의 대응은 이 이슈에 대한 링크를 다는것으로 하면 좋을 것 같습니다.

cj848 commented 2 weeks ago

It cannot be included in the official source, but if you are looking for a solution, you can solve it by implementing Serializer and Configuration and registering the implemented Serializer as shown below. Other detailed Serializer implementation documentation can be found at https://kotlin-jdsl.gitbook.io/docs/jpql-with-kotlin-jdsl/custom-dsl#serializer

open class JpqlFunctionSerializerHibernate65Support {
    protected fun serialize(name: String, args: Iterable<Expression<*>>, writer: JpqlWriter, context: RenderContext) {
        val delegate = context.getValue(JpqlRenderSerializer)
        // name(xxx,xxx,xxx) or name()
        writer.write(name)
        writer.writeParentheses {
            if (IterableUtils.isNotEmpty(args)) {
                writer.writeEach(args, separator = ", ") {
                    delegate.serialize(it, writer, context)
                }
            }
        }
    }
}

class JpqlFunctionPredicateHibernate65Serializer : JpqlFunctionSerializerHibernate65Support(),
    JpqlSerializer<JpqlFunctionPredicate> {
    override fun handledType(): KClass<JpqlFunctionPredicate> {
        return JpqlFunctionPredicate::class
    }

    override fun serialize(part: JpqlFunctionPredicate, writer: JpqlWriter, context: RenderContext) {
        serialize(part.name, part.args, writer, context)
    }
}

class JpqlFunctionExpressionHibernate65Serializer : JpqlFunctionSerializerHibernate65Support(),
    JpqlSerializer<JpqlFunctionExpression<*>> {
    override fun handledType(): KClass<JpqlFunctionExpression<*>> {
        return JpqlFunctionExpression::class
    }

    override fun serialize(part: JpqlFunctionExpression<*>, writer: JpqlWriter, context: RenderContext) {
        serialize(part.name, part.args, writer, context)
    }
}

// if you using spring-data-jpa
@Configuration
open class JdslCustomConfiguration {
    @Bean
    open fun jpqlFunctionPredicateHibernate65Serializer() = JpqlFunctionPredicateHibernate65Serializer()

    @Bean
    open fun jpqlFunctionExpressionHibernate65Serializer() = JpqlFunctionExpressionHibernate65Serializer()
}

--- korean 공식 source에 포함될 수는 없지만, 만약 당신이 해결책을 찾는다면 아래와 같이 Serializer 와 Configuration 을 구현하는 방식, 그리고 구현한 Serializer 를 등록하는 방식으로 해결할 수 있습니다. 그 외의 자세한 Serializer 구현 관련 문서는 https://kotlin-jdsl.gitbook.io/docs/v/ko-1/jpql-with-kotlin-jdsl/custom-dsl#serializer 에서 찾아볼 수 있습니다.