From 1bc08e109e039d786135cd0cec7cdacef3219741 Mon Sep 17 00:00:00 2001 From: Gyeongtae Noh Date: Tue, 6 Feb 2024 17:49:15 +0900 Subject: [PATCH] feat: supporting localDateTime function for datetime function --- docs/en/jpql-with-kotlin-jdsl/expressions.md | 2 +- docs/ko/jpql-with-kotlin-jdsl/expressions.md | 2 +- .../com/linecorp/kotlinjdsl/dsl/jpql/Jpql.kt | 9 ++++ .../jpql/LocalDateTimeExpressionDslTest.kt | 25 +++++++++ .../querymodel/jpql/expression/Expressions.kt | 10 ++++ .../impl/LocalDateTimeExpression.kt | 6 +++ .../jpql/expression/ExpressionsTest.kt | 12 +++++ .../render/jpql/JpqlRenderContext.kt | 2 + .../impl/JpqlLocalDateTimeSerializer.kt | 27 ++++++++++ ...qlLocalDateTimeExpressionSerializerTest.kt | 54 +++++++++++++++++++ 10 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/LocalDateTimeExpressionDslTest.kt create mode 100644 query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/impl/LocalDateTimeExpression.kt create mode 100644 render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlLocalDateTimeSerializer.kt create mode 100644 render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlLocalDateTimeExpressionSerializerTest.kt diff --git a/docs/en/jpql-with-kotlin-jdsl/expressions.md b/docs/en/jpql-with-kotlin-jdsl/expressions.md index ec118d430..4c10c37a9 100644 --- a/docs/en/jpql-with-kotlin-jdsl/expressions.md +++ b/docs/en/jpql-with-kotlin-jdsl/expressions.md @@ -228,7 +228,7 @@ Kotlin JDSL provides a series of functions to support built-in functions in JPA. | CURRENT\_TIMESTAMP | not yet | | LOCAL DATE | not yet | | LOCAL TIME | not yet | -| LOCAL DATETIME | not yet | +| LOCAL DATETIME | support | | EXTRACT | not yet | ### Database function diff --git a/docs/ko/jpql-with-kotlin-jdsl/expressions.md b/docs/ko/jpql-with-kotlin-jdsl/expressions.md index e77eadfec..2b7fab949 100644 --- a/docs/ko/jpql-with-kotlin-jdsl/expressions.md +++ b/docs/ko/jpql-with-kotlin-jdsl/expressions.md @@ -226,7 +226,7 @@ Kotlin JDSL은 JPA에서 제공하는 여러 함수들을 지원하기 위함 | CURRENT\_TIMESTAMP | not yet | | LOCAL DATE | not yet | | LOCAL TIME | not yet | -| LOCAL DATETIME | not yet | +| LOCAL DATETIME | support | | EXTRACT | not yet | ### Database function diff --git a/dsl/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/Jpql.kt b/dsl/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/Jpql.kt index bc4ee6d0a..9f2c8b364 100644 --- a/dsl/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/Jpql.kt +++ b/dsl/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/Jpql.kt @@ -44,6 +44,7 @@ import com.linecorp.kotlinjdsl.querymodel.jpql.select.SelectQuery import com.linecorp.kotlinjdsl.querymodel.jpql.sort.Sort import java.math.BigDecimal import java.math.BigInteger +import java.time.LocalDateTime import kotlin.internal.Exact import kotlin.internal.LowPriorityInOverloadResolution import kotlin.reflect.KClass @@ -166,6 +167,14 @@ open class Jpql : JpqlDsl { return Expressions.nullLiteral() } + /** + * Creates a datetime expression with the localDateTime. + */ + @SinceJdsl("3.4.0") + fun localDateTime(): Expression { + return Expressions.localDateTime() + } + /** * Creates a parameter expression with the name. */ diff --git a/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/LocalDateTimeExpressionDslTest.kt b/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/LocalDateTimeExpressionDslTest.kt new file mode 100644 index 000000000..6cc918217 --- /dev/null +++ b/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/LocalDateTimeExpressionDslTest.kt @@ -0,0 +1,25 @@ +package com.linecorp.kotlinjdsl.dsl.jpql + +import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expression +import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expressions +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import java.time.LocalDateTime + +class LocalDateTimeExpressionDslTest { + + @Test + fun `localDateTime to support LOCAL DATETIME in jpql`() { + // when + val expression = queryPart { + localDateTime() + }.toExpression() + + val actual: Expression = expression // for type check + + // then + val expected = Expressions.localDateTime() + + assertThat(actual).isEqualTo(expected) + } +} diff --git a/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/Expressions.kt b/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/Expressions.kt index 25f378881..7a3ff9872 100644 --- a/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/Expressions.kt +++ b/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/Expressions.kt @@ -38,12 +38,14 @@ import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlTrimLeading import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlTrimTrailing import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlUpper import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlValue +import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.LocalDateTimeExpression import com.linecorp.kotlinjdsl.querymodel.jpql.path.Path import com.linecorp.kotlinjdsl.querymodel.jpql.predicate.Predicate import com.linecorp.kotlinjdsl.querymodel.jpql.select.SelectQuery import com.linecorp.kotlinjdsl.querymodel.jpql.select.impl.JpqlSelectQuery import java.math.BigDecimal import java.math.BigInteger +import java.time.LocalDateTime import kotlin.internal.Exact import kotlin.reflect.KClass @@ -149,6 +151,14 @@ object Expressions { return JpqlLiteral.NullLiteral as Expression } + /** + * Create a LOCAL DATETIME Expression by LocalDateTime. + */ + @SinceJdsl("3.4.0") + fun localDateTime(): Expression { + return LocalDateTimeExpression + } + /** * Creates a parameter expression with the name. */ diff --git a/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/impl/LocalDateTimeExpression.kt b/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/impl/LocalDateTimeExpression.kt new file mode 100644 index 000000000..13d2ca898 --- /dev/null +++ b/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/impl/LocalDateTimeExpression.kt @@ -0,0 +1,6 @@ +package com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl + +import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expression +import java.time.LocalDateTime + +object LocalDateTimeExpression : Expression diff --git a/query-model/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/ExpressionsTest.kt b/query-model/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/ExpressionsTest.kt index fae31a8ba..542c33368 100644 --- a/query-model/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/ExpressionsTest.kt +++ b/query-model/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/ExpressionsTest.kt @@ -40,6 +40,7 @@ import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlTrimLeading import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlTrimTrailing import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlUpper import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlValue +import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.LocalDateTimeExpression import com.linecorp.kotlinjdsl.querymodel.jpql.path.Paths import com.linecorp.kotlinjdsl.querymodel.jpql.predicate.Predicates import com.linecorp.kotlinjdsl.querymodel.jpql.select.impl.JpqlSelectQuery @@ -887,4 +888,15 @@ class ExpressionsTest : WithAssertions { assertThat(actual).isEqualTo(expected) } + + @Test + fun `localDateTime to support LOCAL DATETIME in jpql`() { + // when + val actual = Expressions.localDateTime() + + // then + val expected = LocalDateTimeExpression.toExpression() + + assertThat(actual).isEqualTo(expected) + } } diff --git a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/JpqlRenderContext.kt b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/JpqlRenderContext.kt index ed5e4e135..ce5f6dc5d 100644 --- a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/JpqlRenderContext.kt +++ b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/JpqlRenderContext.kt @@ -69,6 +69,7 @@ import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlLessThanOrEqualTo import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlLessThanSerializer import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlLikeSerializer import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlLiteralSerializer +import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlLocalDateTimeSerializer import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlLocateSerializer import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlLowerSerializer import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlMaxSerializer @@ -314,6 +315,7 @@ private class DefaultModule : JpqlRenderModule { JpqlLessThanSerializer(), JpqlLikeSerializer(), JpqlLiteralSerializer(), + JpqlLocalDateTimeSerializer(), JpqlLocateSerializer(), JpqlLowerSerializer(), JpqlMaxSerializer(), diff --git a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlLocalDateTimeSerializer.kt b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlLocalDateTimeSerializer.kt new file mode 100644 index 000000000..26d2a1a34 --- /dev/null +++ b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlLocalDateTimeSerializer.kt @@ -0,0 +1,27 @@ +package com.linecorp.kotlinjdsl.render.jpql.serializer.impl + +import com.linecorp.kotlinjdsl.Internal +import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.LocalDateTimeExpression +import com.linecorp.kotlinjdsl.render.RenderContext +import com.linecorp.kotlinjdsl.render.jpql.serializer.JpqlRenderSerializer +import com.linecorp.kotlinjdsl.render.jpql.serializer.JpqlSerializer +import com.linecorp.kotlinjdsl.render.jpql.writer.JpqlWriter +import kotlin.reflect.KClass + +@Internal +class JpqlLocalDateTimeSerializer : JpqlSerializer { + + override fun handledType(): KClass { + return LocalDateTimeExpression::class + } + + override fun serialize(part: LocalDateTimeExpression, writer: JpqlWriter, context: RenderContext) { + val delegate = context.getValue(JpqlRenderSerializer) + + writer.write("LOCAL DATETIME") + + writer.writeParentheses { + delegate.serialize(part, writer, context) + } + } +} diff --git a/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlLocalDateTimeExpressionSerializerTest.kt b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlLocalDateTimeExpressionSerializerTest.kt new file mode 100644 index 000000000..098a8d629 --- /dev/null +++ b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlLocalDateTimeExpressionSerializerTest.kt @@ -0,0 +1,54 @@ +package com.linecorp.kotlinjdsl.render.jpql.serializer.impl + +import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expressions +import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.LocalDateTimeExpression +import com.linecorp.kotlinjdsl.render.TestRenderContext +import com.linecorp.kotlinjdsl.render.jpql.serializer.JpqlRenderSerializer +import com.linecorp.kotlinjdsl.render.jpql.serializer.JpqlSerializerTest +import com.linecorp.kotlinjdsl.render.jpql.writer.JpqlWriter +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.just +import io.mockk.runs +import io.mockk.verifySequence +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +@JpqlSerializerTest +class JpqlLocalDateTimeExpressionSerializerTest { + private val sut = JpqlLocalDateTimeSerializer() + + @MockK + private lateinit var writer: JpqlWriter + + @MockK + private lateinit var serializer: JpqlRenderSerializer + + @Test + fun `handle type only JpqlLocalDateTime`() { + // when + val actual = sut.handledType() + + // then + assertThat(actual).isEqualTo(LocalDateTimeExpression::class) + } + + @Test + fun `serialize LOCAL DATETIME function in Jpql`() { + // given + every { writer.write(any()) } just runs + + val part = Expressions.localDateTime() as LocalDateTimeExpression + val context = TestRenderContext(serializer) + + // when + sut.serialize(part, writer, context) + + // then + verifySequence { + writer.write("LOCAL DATETIME") + writer.writeParentheses(any()) + serializer.serialize(part, writer, context) + } + } +}