From 016749677e83f7290638f92295181aa525c4ab40 Mon Sep 17 00:00:00 2001 From: Jake Son Date: Mon, 6 Nov 2023 21:47:28 +0900 Subject: [PATCH 1/8] refactor(query-model): change property type to KCallable --- .../querymodel/jpql/path/impl/JpqlEntityProperty.kt | 6 +++--- .../querymodel/jpql/path/impl/JpqlPathProperty.kt | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/path/impl/JpqlEntityProperty.kt b/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/path/impl/JpqlEntityProperty.kt index 0297c5a75..f7ce0e330 100644 --- a/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/path/impl/JpqlEntityProperty.kt +++ b/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/path/impl/JpqlEntityProperty.kt @@ -3,10 +3,10 @@ package com.linecorp.kotlinjdsl.querymodel.jpql.path.impl import com.linecorp.kotlinjdsl.Internal import com.linecorp.kotlinjdsl.querymodel.jpql.entity.Entity import com.linecorp.kotlinjdsl.querymodel.jpql.path.Path -import kotlin.reflect.KProperty1 +import kotlin.reflect.KCallable @Internal data class JpqlEntityProperty internal constructor( - val entity: Entity<*>, - val property: KProperty1, + val entity: Entity, + val property: KCallable, ) : Path diff --git a/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/path/impl/JpqlPathProperty.kt b/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/path/impl/JpqlPathProperty.kt index 1adb203d5..556efd5cf 100644 --- a/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/path/impl/JpqlPathProperty.kt +++ b/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/path/impl/JpqlPathProperty.kt @@ -2,10 +2,10 @@ package com.linecorp.kotlinjdsl.querymodel.jpql.path.impl import com.linecorp.kotlinjdsl.Internal import com.linecorp.kotlinjdsl.querymodel.jpql.path.Path -import kotlin.reflect.KProperty1 +import kotlin.reflect.KCallable @Internal data class JpqlPathProperty internal constructor( val path: Path, - val property: KProperty1, + val property: KCallable, ) : Path From 9034133a84508ea348293310b3cfece00b19d7e1 Mon Sep 17 00:00:00 2001 From: Jake Son Date: Mon, 6 Nov 2023 22:11:58 +0900 Subject: [PATCH 2/8] feat(render): add property introspector interface --- .../jpql/introspector/CombinedJpqlIntrospector.kt | 11 +++++++++++ .../render/jpql/introspector/JpqlIntrospector.kt | 8 ++++++++ .../jpql/introspector/JpqlPropertyDescription.kt | 12 ++++++++++++ .../introspector/impl/JakartaJpqlIntrospector.kt | 8 +++++++- .../jpql/introspector/impl/JavaxJpqlIntrospector.kt | 8 +++++++- 5 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/JpqlPropertyDescription.kt diff --git a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/CombinedJpqlIntrospector.kt b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/CombinedJpqlIntrospector.kt index b69a43a4a..88b1f5965 100644 --- a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/CombinedJpqlIntrospector.kt +++ b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/CombinedJpqlIntrospector.kt @@ -1,6 +1,7 @@ package com.linecorp.kotlinjdsl.render.jpql.introspector import com.linecorp.kotlinjdsl.SinceJdsl +import kotlin.reflect.KCallable import kotlin.reflect.KClass /** @@ -20,4 +21,14 @@ class CombinedJpqlIntrospector( override fun introspect(type: KClass<*>): JpqlEntityDescription? { return primary.introspect(type) ?: secondary.introspect(type) } + + /** + * Get the entity information by introspecting KCallable. + * + * If the primary introspector introspects this KCallable, it returns the result of the primary introspector. + * Otherwise, it returns the result of the secondary introspector. + */ + override fun introspect(property: KCallable<*>): JpqlPropertyDescription? { + return primary.introspect(property) ?: secondary.introspect(property) + } } diff --git a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/JpqlIntrospector.kt b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/JpqlIntrospector.kt index 564800d74..c4a5ae032 100644 --- a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/JpqlIntrospector.kt +++ b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/JpqlIntrospector.kt @@ -1,6 +1,7 @@ package com.linecorp.kotlinjdsl.render.jpql.introspector import com.linecorp.kotlinjdsl.SinceJdsl +import kotlin.reflect.KCallable import kotlin.reflect.KClass /** @@ -14,4 +15,11 @@ interface JpqlIntrospector { */ @SinceJdsl("3.0.0") fun introspect(type: KClass<*>): JpqlEntityDescription? + + /** + * Introspects the KCallable to get the entity information. + * If it cannot introspect this KCallable, it returns null. + */ + @SinceJdsl("3.1.0") + fun introspect(property: KCallable<*>): JpqlPropertyDescription? } diff --git a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/JpqlPropertyDescription.kt b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/JpqlPropertyDescription.kt new file mode 100644 index 000000000..917d73ab9 --- /dev/null +++ b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/JpqlPropertyDescription.kt @@ -0,0 +1,12 @@ +package com.linecorp.kotlinjdsl.render.jpql.introspector + +import com.linecorp.kotlinjdsl.SinceJdsl + +/** + * Interface to represent the property information. + */ +@SinceJdsl("3.1.0") +interface JpqlPropertyDescription { + @SinceJdsl("3.1.0") + val name: String +} diff --git a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JakartaJpqlIntrospector.kt b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JakartaJpqlIntrospector.kt index eb6f67d27..5be517679 100644 --- a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JakartaJpqlIntrospector.kt +++ b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JakartaJpqlIntrospector.kt @@ -3,11 +3,13 @@ package com.linecorp.kotlinjdsl.render.jpql.introspector.impl import com.linecorp.kotlinjdsl.Internal import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlEntityDescription import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlIntrospector +import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlPropertyDescription +import kotlin.reflect.KCallable import kotlin.reflect.KClass import kotlin.reflect.full.findAnnotations /** - * Introspector that introspects KClass using [jakarta.persistence.Entity]. + * Introspector that introspects KClass and KCallable using [jakarta.persistence.Entity]. */ @Internal class JakartaJpqlIntrospector : JpqlIntrospector { @@ -20,6 +22,10 @@ class JakartaJpqlIntrospector : JpqlIntrospector { null } } + + override fun introspect(property: KCallable<*>): JpqlPropertyDescription? { + TODO("Not yet implemented") + } } private data class JakartaEntity( diff --git a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JavaxJpqlIntrospector.kt b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JavaxJpqlIntrospector.kt index a60bf18c6..a4a84f770 100644 --- a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JavaxJpqlIntrospector.kt +++ b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JavaxJpqlIntrospector.kt @@ -3,11 +3,13 @@ package com.linecorp.kotlinjdsl.render.jpql.introspector.impl import com.linecorp.kotlinjdsl.Internal import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlEntityDescription import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlIntrospector +import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlPropertyDescription +import kotlin.reflect.KCallable import kotlin.reflect.KClass import kotlin.reflect.full.findAnnotations /** - * Introspector that introspects KClass using [javax.persistence.Entity]. + * Introspector that introspects KClass and KCallable using [javax.persistence.Entity]. */ @Internal class JavaxJpqlIntrospector : JpqlIntrospector { @@ -20,6 +22,10 @@ class JavaxJpqlIntrospector : JpqlIntrospector { null } } + + override fun introspect(property: KCallable<*>): JpqlPropertyDescription? { + TODO("Not yet implemented") + } } private data class JavaxEntity( From 8f630366831b789238d663dc697bc50e94b18e42 Mon Sep 17 00:00:00 2001 From: Jake Son Date: Mon, 6 Nov 2023 22:34:17 +0900 Subject: [PATCH 3/8] test(render): add property introspect test case --- .../CombinedJpqlIntrospectorTest.kt | 50 +++++++++++++++++-- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/CombinedJpqlIntrospectorTest.kt b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/CombinedJpqlIntrospectorTest.kt index 3ff8d2366..e8d529949 100644 --- a/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/CombinedJpqlIntrospectorTest.kt +++ b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/CombinedJpqlIntrospectorTest.kt @@ -9,6 +9,8 @@ import org.assertj.core.api.WithAssertions import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith +import kotlin.reflect.KCallable +import kotlin.reflect.KClass @ExtendWith(MockKExtension::class) class CombinedJpqlIntrospectorTest : WithAssertions { @@ -24,6 +26,10 @@ class CombinedJpqlIntrospectorTest : WithAssertions { override val name: String = "entityName1" } + private val propertyDescription1 = object : JpqlPropertyDescription { + override val name: String = "propertyName1" + } + @BeforeEach fun setUp() { sut = CombinedJpqlIntrospector( @@ -33,9 +39,9 @@ class CombinedJpqlIntrospectorTest : WithAssertions { } @Test - fun `introspect() return the description of the primary, when the primary returns non null`() { + fun `introspect(type) return the description of the primary, when the primary returns non null`() { // given - every { introspector1.introspect(any()) } returns entityDescription1 + every { introspector1.introspect(any>()) } returns entityDescription1 // when val actual = sut.introspect(Book::class) @@ -49,10 +55,10 @@ class CombinedJpqlIntrospectorTest : WithAssertions { } @Test - fun `introspect() return the description of the secondary, when the primary returns null`() { + fun `introspect(type) return the description of the secondary, when the primary returns null`() { // given - every { introspector1.introspect(any()) } returns null - every { introspector2.introspect(any()) } returns entityDescription1 + every { introspector1.introspect(any>()) } returns null + every { introspector2.introspect(any>()) } returns entityDescription1 // when val actual = sut.introspect(Book::class) @@ -65,4 +71,38 @@ class CombinedJpqlIntrospectorTest : WithAssertions { introspector2.introspect(Book::class) } } + + @Test + fun `introspect(property) return the description of the primary, when the primary returns non null`() { + // given + every { introspector1.introspect(any>()) } returns propertyDescription1 + + // when + val actual = sut.introspect(Book::title) + + // then + assertThat(actual).isEqualTo(propertyDescription1) + + verifySequence { + introspector1.introspect(Book::title) + } + } + + @Test + fun `introspect(property) return the description of the secondary, when the primary returns null`() { + // given + every { introspector1.introspect(any>()) } returns null + every { introspector2.introspect(any>()) } returns propertyDescription1 + + // when + val actual = sut.introspect(Book::title) + + // then + assertThat(actual).isEqualTo(propertyDescription1) + + verifySequence { + introspector1.introspect(Book::title) + introspector2.introspect(Book::title) + } + } } From c9ba4d42cb38d41b082e381065519eeb0a2cdec0 Mon Sep 17 00:00:00 2001 From: Jake Son Date: Wed, 8 Nov 2023 22:17:30 +0900 Subject: [PATCH 4/8] feat(render): implement introspect for KCallable --- .../impl/JakartaJpqlIntrospector.kt | 15 +++- .../impl/JavaxJpqlIntrospector.kt | 15 +++- .../jpql/introspector/impl/JpqlProperty.kt | 7 ++ .../impl/JakartaJpqlIntrospectorTest.kt | 78 ++++++++++++++++++- .../impl/JavaxJpqlIntrospectorTest.kt | 74 +++++++++++++++++- 5 files changed, 181 insertions(+), 8 deletions(-) create mode 100644 render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JpqlProperty.kt diff --git a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JakartaJpqlIntrospector.kt b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JakartaJpqlIntrospector.kt index 5be517679..82a8e9a4d 100644 --- a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JakartaJpqlIntrospector.kt +++ b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JakartaJpqlIntrospector.kt @@ -6,6 +6,8 @@ import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlIntrospector import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlPropertyDescription import kotlin.reflect.KCallable import kotlin.reflect.KClass +import kotlin.reflect.KFunction1 +import kotlin.reflect.KProperty1 import kotlin.reflect.full.findAnnotations /** @@ -24,8 +26,19 @@ class JakartaJpqlIntrospector : JpqlIntrospector { } override fun introspect(property: KCallable<*>): JpqlPropertyDescription? { - TODO("Not yet implemented") + return when (property) { + is KProperty1<*, *> -> JpqlProperty(property.name) + is KFunction1<*, *> -> JpqlProperty(resolvePropertyName(property)) + else -> null + } } + + private fun resolvePropertyName(getter: KFunction1<*, *>): String = + if (getter.name.startsWith("is")) { + getter.name + } else { + getter.name.removePrefix("get").replaceFirstChar { it.lowercase() } + } } private data class JakartaEntity( diff --git a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JavaxJpqlIntrospector.kt b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JavaxJpqlIntrospector.kt index a4a84f770..1cd7c6f87 100644 --- a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JavaxJpqlIntrospector.kt +++ b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JavaxJpqlIntrospector.kt @@ -6,6 +6,8 @@ import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlIntrospector import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlPropertyDescription import kotlin.reflect.KCallable import kotlin.reflect.KClass +import kotlin.reflect.KFunction1 +import kotlin.reflect.KProperty1 import kotlin.reflect.full.findAnnotations /** @@ -24,8 +26,19 @@ class JavaxJpqlIntrospector : JpqlIntrospector { } override fun introspect(property: KCallable<*>): JpqlPropertyDescription? { - TODO("Not yet implemented") + return when (property) { + is KProperty1<*, *> -> JpqlProperty(property.name) + is KFunction1<*, *> -> JpqlProperty(resolvePropertyName(property)) + else -> null + } } + + private fun resolvePropertyName(getter: KFunction1<*, *>): String = + if (getter.name.startsWith("is")) { + getter.name + } else { + getter.name.removePrefix("get").replaceFirstChar { it.lowercase() } + } } private data class JavaxEntity( diff --git a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JpqlProperty.kt b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JpqlProperty.kt new file mode 100644 index 000000000..47e2aae20 --- /dev/null +++ b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JpqlProperty.kt @@ -0,0 +1,7 @@ +package com.linecorp.kotlinjdsl.render.jpql.introspector.impl + +import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlPropertyDescription + +internal data class JpqlProperty( + override val name: String, +) : JpqlPropertyDescription diff --git a/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JakartaJpqlIntrospectorTest.kt b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JakartaJpqlIntrospectorTest.kt index 151b67866..0b5962e58 100644 --- a/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JakartaJpqlIntrospectorTest.kt +++ b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JakartaJpqlIntrospectorTest.kt @@ -1,7 +1,9 @@ package com.linecorp.kotlinjdsl.render.jpql.introspector.impl +import io.mockk.mockkClass import org.assertj.core.api.WithAssertions import org.junit.jupiter.api.Test +import kotlin.reflect.KProperty0 class JakartaJpqlIntrospectorTest : WithAssertions { private val sut = JakartaJpqlIntrospector() @@ -11,7 +13,7 @@ class JakartaJpqlIntrospectorTest : WithAssertions { } @Test - fun introspect() { + fun `introspect(type) returns name of entity annotation, when entity annotation has name`() { // given val type = EntityClass1::class @@ -23,7 +25,7 @@ class JakartaJpqlIntrospectorTest : WithAssertions { } @Test - fun `introspect() returns name of class, when entity annotation does not have name`() { + fun `introspect(type) returns name of class, when entity annotation does not have name`() { // given val type = EntityClass2::class @@ -35,7 +37,7 @@ class JakartaJpqlIntrospectorTest : WithAssertions { } @Test - fun `introspect() returns null, when there is no entity annotation`() { + fun `introspect(type) returns null, when there is no entity annotation`() { // given val type = NonEntityCLass1::class @@ -46,8 +48,76 @@ class JakartaJpqlIntrospectorTest : WithAssertions { assertThat(actual?.name).isNull() } + @Test + fun `introspect(property) returns property name, when property is KProperty1`() { + // given + val property = EntityClass1::property1 + + // when + val actual = sut.introspect(property) + + // then + assertThat(actual?.name).isEqualTo("property1") + } + + @Test + fun `introspect(property) returns property name with prefix removed, when getter name starts with get`() { + // given + val property = EntityClass1::getProperty2 + + // when + val actual = sut.introspect(property) + + // then + assertThat(actual?.name).isEqualTo("property2") + } + + @Test + fun `introspect(property) returns property name as is, when getter name starts with is`() { + // given + val property = EntityClass1::isProperty3 + + // when + val actual = sut.introspect(property) + + // then + assertThat(actual?.name).isEqualTo("isProperty3") + } + + @Test + fun `introspect(property) returns property name as is, when getter name does not start with get or is`() { + // given + val property = EntityClass1::someProperty + + // when + val actual = sut.introspect(property) + + // then + assertThat(actual?.name).isEqualTo("someProperty") + } + + @Test + fun `introspect(property) returns null, when property is not KProperty1 or KFunction1`() { + // given + val property = mockkClass(KProperty0::class) + + // when + val actual = sut.introspect(property) + + // then + assertThat(actual?.name).isNull() + } + @jakarta.persistence.Entity(name = ENTITY_NAME_1) - private open class EntityClass1 + private open class EntityClass1 { + val property1: Long = 0 + + fun getProperty2(): Long = 100 + + fun isProperty3(): Boolean = true + + fun someProperty(): String = "someProperty" + } @jakarta.persistence.Entity private open class EntityClass2 diff --git a/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JavaxJpqlIntrospectorTest.kt b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JavaxJpqlIntrospectorTest.kt index 46e8b1dac..f8d70a923 100644 --- a/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JavaxJpqlIntrospectorTest.kt +++ b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JavaxJpqlIntrospectorTest.kt @@ -1,7 +1,9 @@ package com.linecorp.kotlinjdsl.render.jpql.introspector.impl +import io.mockk.mockkClass import org.assertj.core.api.WithAssertions import org.junit.jupiter.api.Test +import kotlin.reflect.KProperty0 class JavaxJpqlIntrospectorTest : WithAssertions { private val sut = JavaxJpqlIntrospector() @@ -11,7 +13,7 @@ class JavaxJpqlIntrospectorTest : WithAssertions { } @Test - fun introspect() { + fun `introspect(type) returns name of entity annotation, when entity annotation has name`() { // given val type = EntityClass1::class @@ -46,8 +48,76 @@ class JavaxJpqlIntrospectorTest : WithAssertions { assertThat(actual?.name).isNull() } + @Test + fun `introspect(property) returns property name, when property is KProperty1`() { + // given + val property = EntityClass1::property1 + + // when + val actual = sut.introspect(property) + + // then + assertThat(actual?.name).isEqualTo("property1") + } + + @Test + fun `introspect(property) returns property name with prefix removed, when getter name starts with get`() { + // given + val property = EntityClass1::getProperty2 + + // when + val actual = sut.introspect(property) + + // then + assertThat(actual?.name).isEqualTo("property2") + } + + @Test + fun `introspect(property) returns property name as is, when getter name starts with is`() { + // given + val property = EntityClass1::isProperty3 + + // when + val actual = sut.introspect(property) + + // then + assertThat(actual?.name).isEqualTo("isProperty3") + } + + @Test + fun `introspect(property) returns property name as is, when getter name does not start with get or is`() { + // given + val property = EntityClass1::someProperty + + // when + val actual = sut.introspect(property) + + // then + assertThat(actual?.name).isEqualTo("someProperty") + } + + @Test + fun `introspect(property) returns null, when property is not KProperty1 or KFunction1`() { + // given + val property = mockkClass(KProperty0::class) + + // when + val actual = sut.introspect(property) + + // then + assertThat(actual?.name).isNull() + } + @javax.persistence.Entity(name = ENTITY_NAME_1) - private open class EntityClass1 + private open class EntityClass1 { + val property1: Long = 0 + + fun getProperty2(): Long = 100 + + fun isProperty3(): Boolean = true + + fun someProperty(): String = "someProperty" + } @javax.persistence.Entity private open class EntityClass2 From cccf3fb921aadbb9391f2e24821b5e4ca5c8c8b5 Mon Sep 17 00:00:00 2001 From: Jake Son Date: Wed, 8 Nov 2023 22:31:48 +0900 Subject: [PATCH 5/8] feat(render): add introspect method for KCallable --- .../introspector/JpqlRenderIntrospector.kt | 25 +++++++++++++++++-- .../impl/JpqlEntitySerializerTest.kt | 3 ++- .../impl/JpqlEntityTreatSerializerTest.kt | 5 ++-- .../impl/JpqlPathTreatSerializerTest.kt | 3 ++- .../impl/JpqlValueSerializerTest.kt | 3 ++- 5 files changed, 32 insertions(+), 7 deletions(-) diff --git a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/JpqlRenderIntrospector.kt b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/JpqlRenderIntrospector.kt index 0c2c412f2..d5c60ab59 100644 --- a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/JpqlRenderIntrospector.kt +++ b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/JpqlRenderIntrospector.kt @@ -4,6 +4,7 @@ import com.linecorp.kotlinjdsl.SinceJdsl import com.linecorp.kotlinjdsl.render.AbstractRenderContextElement import com.linecorp.kotlinjdsl.render.RenderContext import java.util.concurrent.ConcurrentHashMap +import kotlin.reflect.KCallable import kotlin.reflect.KClass /** @@ -15,7 +16,8 @@ class JpqlRenderIntrospector( ) : AbstractRenderContextElement(Key) { companion object Key : RenderContext.Key - private val tableLookupCache: MutableMap, JpqlEntityDescription> = ConcurrentHashMap() + private val classTableLookupCache: MutableMap, JpqlEntityDescription> = ConcurrentHashMap() + private val propertyTableLookupCache: MutableMap, JpqlPropertyDescription> = ConcurrentHashMap() /** * Creates a new introspector by combining this introspector and the introspector. @@ -38,8 +40,22 @@ class JpqlRenderIntrospector( return getCachedDescription(clazz) } + /** + * Introspects the KCallable to get the property information. + */ + @SinceJdsl("3.1.0") + fun introspect(property: KCallable<*>): JpqlPropertyDescription { + return getCachedDescription(property) + } + private fun getCachedDescription(clazz: KClass<*>): JpqlEntityDescription { - return tableLookupCache.computeIfAbsent(clazz) { + return classTableLookupCache.computeIfAbsent(clazz) { + getDescription(it) + } + } + + private fun getCachedDescription(property: KCallable<*>): JpqlPropertyDescription { + return propertyTableLookupCache.computeIfAbsent(property) { getDescription(it) } } @@ -48,4 +64,9 @@ class JpqlRenderIntrospector( return introspector.introspect(clazz) ?: throw IllegalStateException("There is no description for ${clazz.java.name}") } + + private fun getDescription(property: KCallable<*>): JpqlPropertyDescription { + return introspector.introspect(property) + ?: throw IllegalStateException("There is no description for ${property.name}") + } } diff --git a/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlEntitySerializerTest.kt b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlEntitySerializerTest.kt index 9835dd9c8..c022a7bad 100644 --- a/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlEntitySerializerTest.kt +++ b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlEntitySerializerTest.kt @@ -18,6 +18,7 @@ import io.mockk.verifySequence import org.assertj.core.api.WithAssertions import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest +import kotlin.reflect.KClass @JpqlSerializerTest internal class JpqlEntitySerializerTest : WithAssertions { @@ -57,7 +58,7 @@ internal class JpqlEntitySerializerTest : WithAssertions { clause: JpqlRenderClause, ) { // given - every { introspector.introspect(any()) } returns entityDescription1 + every { introspector.introspect(any>()) } returns entityDescription1 val part = Entities.entity(Book::class, alias1) val context = TestRenderContext(introspector, statement, clause) diff --git a/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlEntityTreatSerializerTest.kt b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlEntityTreatSerializerTest.kt index 53c04122c..d4c98a7bb 100644 --- a/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlEntityTreatSerializerTest.kt +++ b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlEntityTreatSerializerTest.kt @@ -19,6 +19,7 @@ import io.mockk.verifySequence import org.assertj.core.api.WithAssertions import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest +import kotlin.reflect.KClass @JpqlSerializerTest class JpqlEntityTreatSerializerTest : WithAssertions { @@ -58,7 +59,7 @@ class JpqlEntityTreatSerializerTest : WithAssertions { clause: JpqlRenderClause, ) { // given - every { introspector.introspect(any()) } returns entityDescription1 + every { introspector.introspect(any>()) } returns entityDescription1 val part = Entities.treat( entity1, @@ -93,7 +94,7 @@ class JpqlEntityTreatSerializerTest : WithAssertions { clause: JpqlRenderClause, ) { // given - every { introspector.introspect(any()) } returns entityDescription1 + every { introspector.introspect(any>()) } returns entityDescription1 val part = Entities.treat( entity1, diff --git a/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlPathTreatSerializerTest.kt b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlPathTreatSerializerTest.kt index 6c42ec983..ebf723642 100644 --- a/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlPathTreatSerializerTest.kt +++ b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlPathTreatSerializerTest.kt @@ -15,6 +15,7 @@ import io.mockk.impl.annotations.MockK import io.mockk.verifySequence import org.assertj.core.api.WithAssertions import org.junit.jupiter.api.Test +import kotlin.reflect.KClass @JpqlSerializerTest class JpqlPathTreatSerializerTest : WithAssertions { @@ -47,7 +48,7 @@ class JpqlPathTreatSerializerTest : WithAssertions { @Test fun serialize() { // given - every { introspector.introspect(any()) } returns entityDescription1 + every { introspector.introspect(any>()) } returns entityDescription1 val part = Paths.treat( path1, diff --git a/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlValueSerializerTest.kt b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlValueSerializerTest.kt index 67450a9bb..494539b2f 100644 --- a/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlValueSerializerTest.kt +++ b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlValueSerializerTest.kt @@ -13,6 +13,7 @@ import io.mockk.impl.annotations.MockK import io.mockk.verifySequence import org.assertj.core.api.WithAssertions import org.junit.jupiter.api.Test +import kotlin.reflect.KClass @JpqlSerializerTest class JpqlValueSerializerTest : WithAssertions { @@ -59,7 +60,7 @@ class JpqlValueSerializerTest : WithAssertions { @Test fun `serialize() draws entity name, when value is KClass`() { // given - every { introspector.introspect(any()) } returns entityDescription1 + every { introspector.introspect(any>()) } returns entityDescription1 val part = Expressions.value( Book::class, From 4e145d2bc390a40aa24dafcfc86e32c086f5d373 Mon Sep 17 00:00:00 2001 From: Jake Son Date: Wed, 8 Nov 2023 22:39:53 +0900 Subject: [PATCH 6/8] feat(render): get property name by using introspector --- .../impl/JpqlEntityPropertySerializer.kt | 6 +++++- .../impl/JpqlPathPropertySerializer.kt | 5 ++++- .../impl/JpqlEntityPropertySerializerTest.kt | 16 ++++++++++++++-- .../impl/JpqlPathPropertySerializerTest.kt | 17 +++++++++++++++-- 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlEntityPropertySerializer.kt b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlEntityPropertySerializer.kt index d3d89c177..87f599c37 100644 --- a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlEntityPropertySerializer.kt +++ b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlEntityPropertySerializer.kt @@ -3,6 +3,7 @@ package com.linecorp.kotlinjdsl.render.jpql.serializer.impl import com.linecorp.kotlinjdsl.Internal import com.linecorp.kotlinjdsl.querymodel.jpql.path.impl.JpqlEntityProperty import com.linecorp.kotlinjdsl.render.RenderContext +import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlRenderIntrospector import com.linecorp.kotlinjdsl.render.jpql.serializer.JpqlSerializer import com.linecorp.kotlinjdsl.render.jpql.writer.JpqlWriter import kotlin.reflect.KClass @@ -14,8 +15,11 @@ class JpqlEntityPropertySerializer : JpqlSerializer> { } override fun serialize(part: JpqlEntityProperty<*, *>, writer: JpqlWriter, context: RenderContext) { + val introspector = context.getValue(JpqlRenderIntrospector) + val property = introspector.introspect(part.property) + writer.write(part.entity.alias) writer.write(".") - writer.write(part.property.name) + writer.write(property.name) } } diff --git a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlPathPropertySerializer.kt b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlPathPropertySerializer.kt index c2939a3b7..954376b2e 100644 --- a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlPathPropertySerializer.kt +++ b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlPathPropertySerializer.kt @@ -3,6 +3,7 @@ package com.linecorp.kotlinjdsl.render.jpql.serializer.impl import com.linecorp.kotlinjdsl.Internal import com.linecorp.kotlinjdsl.querymodel.jpql.path.impl.JpqlPathProperty import com.linecorp.kotlinjdsl.render.RenderContext +import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlRenderIntrospector import com.linecorp.kotlinjdsl.render.jpql.serializer.JpqlRenderSerializer import com.linecorp.kotlinjdsl.render.jpql.serializer.JpqlSerializer import com.linecorp.kotlinjdsl.render.jpql.writer.JpqlWriter @@ -16,9 +17,11 @@ class JpqlPathPropertySerializer : JpqlSerializer> { override fun serialize(part: JpqlPathProperty<*, *>, writer: JpqlWriter, context: RenderContext) { val delegate = context.getValue(JpqlRenderSerializer) + val introspector = context.getValue(JpqlRenderIntrospector) + val property = introspector.introspect(part.property) delegate.serialize(part.path, writer, context) writer.write(".") - writer.write(part.property.name) + writer.write(property.name) } } diff --git a/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlEntityPropertySerializerTest.kt b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlEntityPropertySerializerTest.kt index 2dcaa0736..d89a63203 100644 --- a/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlEntityPropertySerializerTest.kt +++ b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlEntityPropertySerializerTest.kt @@ -5,13 +5,17 @@ import com.linecorp.kotlinjdsl.querymodel.jpql.path.Paths import com.linecorp.kotlinjdsl.querymodel.jpql.path.impl.JpqlEntityProperty import com.linecorp.kotlinjdsl.render.TestRenderContext import com.linecorp.kotlinjdsl.render.jpql.entity.book.Book +import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlPropertyDescription +import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlRenderIntrospector 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.verifySequence import org.assertj.core.api.WithAssertions import org.junit.jupiter.api.Test +import kotlin.reflect.KCallable @JpqlSerializerTest class JpqlEntityPropertySerializerTest : WithAssertions { @@ -20,11 +24,17 @@ class JpqlEntityPropertySerializerTest : WithAssertions { @MockK private lateinit var writer: JpqlWriter + @MockK + private lateinit var introspector: JpqlRenderIntrospector + @MockK private lateinit var serializer: JpqlRenderSerializer private val entity1 = Entities.entity(Book::class, "book01") private val property1 = Book::price + private val propertyDescription1 = object : JpqlPropertyDescription { + override val name = property1.name + } @Test fun handledType() { @@ -38,11 +48,13 @@ class JpqlEntityPropertySerializerTest : WithAssertions { @Test fun serialize() { // given + every { introspector.introspect(any>()) } returns propertyDescription1 + val part = Paths.path( entity1, property1, ) - val context = TestRenderContext(serializer) + val context = TestRenderContext(introspector, serializer) // when sut.serialize(part as JpqlEntityProperty<*, *>, writer, context) @@ -51,7 +63,7 @@ class JpqlEntityPropertySerializerTest : WithAssertions { verifySequence { writer.write(entity1.alias) writer.write(".") - writer.write(property1.name) + writer.write(propertyDescription1.name) } } } diff --git a/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlPathPropertySerializerTest.kt b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlPathPropertySerializerTest.kt index ee3eabc37..cc8cc1aeb 100644 --- a/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlPathPropertySerializerTest.kt +++ b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlPathPropertySerializerTest.kt @@ -5,13 +5,17 @@ import com.linecorp.kotlinjdsl.querymodel.jpql.path.impl.JpqlPathProperty import com.linecorp.kotlinjdsl.render.TestRenderContext import com.linecorp.kotlinjdsl.render.jpql.entity.book.Book import com.linecorp.kotlinjdsl.render.jpql.entity.book.Isbn +import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlPropertyDescription +import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlRenderIntrospector 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.verifySequence import org.assertj.core.api.WithAssertions import org.junit.jupiter.api.Test +import kotlin.reflect.KCallable @JpqlSerializerTest class JpqlPathPropertySerializerTest : WithAssertions { @@ -20,6 +24,9 @@ class JpqlPathPropertySerializerTest : WithAssertions { @MockK private lateinit var writer: JpqlWriter + @MockK + private lateinit var introspector: JpqlRenderIntrospector + @MockK private lateinit var serializer: JpqlRenderSerializer @@ -27,6 +34,10 @@ class JpqlPathPropertySerializerTest : WithAssertions { private val property1 = Isbn::value + private val propertyDescription1 = object : JpqlPropertyDescription { + override val name = property1.name + } + @Test fun handledType() { // when @@ -39,11 +50,13 @@ class JpqlPathPropertySerializerTest : WithAssertions { @Test fun serialize() { // given + every { introspector.introspect(any>()) } returns propertyDescription1 + val part = Paths.path( path1, property1, ) - val context = TestRenderContext(serializer) + val context = TestRenderContext(introspector, serializer) // when sut.serialize(part as JpqlPathProperty<*, *>, writer, context) @@ -52,7 +65,7 @@ class JpqlPathPropertySerializerTest : WithAssertions { verifySequence { serializer.serialize(path1, writer, context) writer.write(".") - writer.write(property1.name) + writer.write(propertyDescription1.name) } } } From 3a2fbc74846eb73cf7dad6fca01e86256ea0226d Mon Sep 17 00:00:00 2001 From: Jake Son Date: Fri, 10 Nov 2023 08:12:14 +0900 Subject: [PATCH 7/8] refactor(render): change property name --- .../render/jpql/introspector/JpqlRenderIntrospector.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/JpqlRenderIntrospector.kt b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/JpqlRenderIntrospector.kt index d5c60ab59..c012aade9 100644 --- a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/JpqlRenderIntrospector.kt +++ b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/JpqlRenderIntrospector.kt @@ -16,8 +16,8 @@ class JpqlRenderIntrospector( ) : AbstractRenderContextElement(Key) { companion object Key : RenderContext.Key - private val classTableLookupCache: MutableMap, JpqlEntityDescription> = ConcurrentHashMap() - private val propertyTableLookupCache: MutableMap, JpqlPropertyDescription> = ConcurrentHashMap() + private val entityLookupCache: MutableMap, JpqlEntityDescription> = ConcurrentHashMap() + private val propertyLookupCache: MutableMap, JpqlPropertyDescription> = ConcurrentHashMap() /** * Creates a new introspector by combining this introspector and the introspector. @@ -49,13 +49,13 @@ class JpqlRenderIntrospector( } private fun getCachedDescription(clazz: KClass<*>): JpqlEntityDescription { - return classTableLookupCache.computeIfAbsent(clazz) { + return entityLookupCache.computeIfAbsent(clazz) { getDescription(it) } } private fun getCachedDescription(property: KCallable<*>): JpqlPropertyDescription { - return propertyTableLookupCache.computeIfAbsent(property) { + return propertyLookupCache.computeIfAbsent(property) { getDescription(it) } } From 9de2e4ad43548f2efb851210033e83530de75278 Mon Sep 17 00:00:00 2001 From: Jake Son Date: Fri, 10 Nov 2023 08:36:03 +0900 Subject: [PATCH 8/8] refactor(render): separate property and class introspector --- .../render/jpql/JpqlRenderContext.kt | 3 + .../introspector/JpqlEntityIntrospector.kt | 12 +++ .../introspector/JpqlPropertyIntrospector.kt | 12 +++ .../impl/JakartaJpqlIntrospector.kt | 25 +----- .../impl/JavaxJpqlIntrospector.kt | 25 +----- .../jpql/introspector/impl/JpqlProperty.kt | 7 -- .../KotlinStyleJpqlPropertyIntrospector.kt | 33 ++++++++ .../impl/JakartaJpqlIntrospectorTest.kt | 78 +----------------- .../impl/JavaxJpqlIntrospectorTest.kt | 74 +---------------- ...KotlinStyleJpqlPropertyIntrospectorTest.kt | 80 +++++++++++++++++++ 10 files changed, 152 insertions(+), 197 deletions(-) create mode 100644 render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/JpqlEntityIntrospector.kt create mode 100644 render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/JpqlPropertyIntrospector.kt delete mode 100644 render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JpqlProperty.kt create mode 100644 render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/KotlinStyleJpqlPropertyIntrospector.kt create mode 100644 render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/KotlinStyleJpqlPropertyIntrospectorTest.kt 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 47380d5b0..422be8953 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 @@ -9,6 +9,7 @@ import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlIntrospectorModifier import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlRenderIntrospector import com.linecorp.kotlinjdsl.render.jpql.introspector.impl.JakartaJpqlIntrospector import com.linecorp.kotlinjdsl.render.jpql.introspector.impl.JavaxJpqlIntrospector +import com.linecorp.kotlinjdsl.render.jpql.introspector.impl.KotlinStyleJpqlPropertyIntrospector import com.linecorp.kotlinjdsl.render.jpql.serializer.JpqlRenderSerializer import com.linecorp.kotlinjdsl.render.jpql.serializer.JpqlSerializer import com.linecorp.kotlinjdsl.render.jpql.serializer.JpqlSerializerModifier @@ -246,6 +247,8 @@ private class DefaultModule : JpqlRenderModule { context.appendIntrospector(JakartaJpqlIntrospector()) } + context.appendIntrospector(KotlinStyleJpqlPropertyIntrospector()) + context.addAllSerializer( JpqlAliasedExpressionSerializer(), JpqlAndSerializer(), diff --git a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/JpqlEntityIntrospector.kt b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/JpqlEntityIntrospector.kt new file mode 100644 index 000000000..30024b3b4 --- /dev/null +++ b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/JpqlEntityIntrospector.kt @@ -0,0 +1,12 @@ +package com.linecorp.kotlinjdsl.render.jpql.introspector + +import com.linecorp.kotlinjdsl.SinceJdsl +import kotlin.reflect.KCallable + +/** + * Abstract class to get the entity information by introspecting KClass. + */ +@SinceJdsl("3.1.0") +abstract class JpqlEntityIntrospector : JpqlIntrospector { + override fun introspect(property: KCallable<*>): JpqlPropertyDescription? = null +} diff --git a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/JpqlPropertyIntrospector.kt b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/JpqlPropertyIntrospector.kt new file mode 100644 index 000000000..99347270e --- /dev/null +++ b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/JpqlPropertyIntrospector.kt @@ -0,0 +1,12 @@ +package com.linecorp.kotlinjdsl.render.jpql.introspector + +import com.linecorp.kotlinjdsl.SinceJdsl +import kotlin.reflect.KClass + +/** + * Abstract class to get the entity information by introspecting KCallable. + */ +@SinceJdsl("3.1.0") +abstract class JpqlPropertyIntrospector : JpqlIntrospector { + override fun introspect(type: KClass<*>): JpqlEntityDescription? = null +} diff --git a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JakartaJpqlIntrospector.kt b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JakartaJpqlIntrospector.kt index 82a8e9a4d..b7f75b54b 100644 --- a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JakartaJpqlIntrospector.kt +++ b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JakartaJpqlIntrospector.kt @@ -2,19 +2,15 @@ package com.linecorp.kotlinjdsl.render.jpql.introspector.impl import com.linecorp.kotlinjdsl.Internal import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlEntityDescription -import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlIntrospector -import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlPropertyDescription -import kotlin.reflect.KCallable +import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlEntityIntrospector import kotlin.reflect.KClass -import kotlin.reflect.KFunction1 -import kotlin.reflect.KProperty1 import kotlin.reflect.full.findAnnotations /** - * Introspector that introspects KClass and KCallable using [jakarta.persistence.Entity]. + * Introspector that introspects KClass using [jakarta.persistence.Entity]. */ @Internal -class JakartaJpqlIntrospector : JpqlIntrospector { +class JakartaJpqlIntrospector : JpqlEntityIntrospector() { override fun introspect(type: KClass<*>): JpqlEntityDescription? { val entity = type.findAnnotations(jakarta.persistence.Entity::class).firstOrNull() @@ -24,21 +20,6 @@ class JakartaJpqlIntrospector : JpqlIntrospector { null } } - - override fun introspect(property: KCallable<*>): JpqlPropertyDescription? { - return when (property) { - is KProperty1<*, *> -> JpqlProperty(property.name) - is KFunction1<*, *> -> JpqlProperty(resolvePropertyName(property)) - else -> null - } - } - - private fun resolvePropertyName(getter: KFunction1<*, *>): String = - if (getter.name.startsWith("is")) { - getter.name - } else { - getter.name.removePrefix("get").replaceFirstChar { it.lowercase() } - } } private data class JakartaEntity( diff --git a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JavaxJpqlIntrospector.kt b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JavaxJpqlIntrospector.kt index 1cd7c6f87..3effe861d 100644 --- a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JavaxJpqlIntrospector.kt +++ b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JavaxJpqlIntrospector.kt @@ -2,19 +2,15 @@ package com.linecorp.kotlinjdsl.render.jpql.introspector.impl import com.linecorp.kotlinjdsl.Internal import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlEntityDescription -import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlIntrospector -import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlPropertyDescription -import kotlin.reflect.KCallable +import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlEntityIntrospector import kotlin.reflect.KClass -import kotlin.reflect.KFunction1 -import kotlin.reflect.KProperty1 import kotlin.reflect.full.findAnnotations /** - * Introspector that introspects KClass and KCallable using [javax.persistence.Entity]. + * Introspector that introspects KClass using [javax.persistence.Entity]. */ @Internal -class JavaxJpqlIntrospector : JpqlIntrospector { +class JavaxJpqlIntrospector : JpqlEntityIntrospector() { override fun introspect(type: KClass<*>): JpqlEntityDescription? { val entity = type.findAnnotations(javax.persistence.Entity::class).firstOrNull() @@ -24,21 +20,6 @@ class JavaxJpqlIntrospector : JpqlIntrospector { null } } - - override fun introspect(property: KCallable<*>): JpqlPropertyDescription? { - return when (property) { - is KProperty1<*, *> -> JpqlProperty(property.name) - is KFunction1<*, *> -> JpqlProperty(resolvePropertyName(property)) - else -> null - } - } - - private fun resolvePropertyName(getter: KFunction1<*, *>): String = - if (getter.name.startsWith("is")) { - getter.name - } else { - getter.name.removePrefix("get").replaceFirstChar { it.lowercase() } - } } private data class JavaxEntity( diff --git a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JpqlProperty.kt b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JpqlProperty.kt deleted file mode 100644 index 47e2aae20..000000000 --- a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JpqlProperty.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.linecorp.kotlinjdsl.render.jpql.introspector.impl - -import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlPropertyDescription - -internal data class JpqlProperty( - override val name: String, -) : JpqlPropertyDescription diff --git a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/KotlinStyleJpqlPropertyIntrospector.kt b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/KotlinStyleJpqlPropertyIntrospector.kt new file mode 100644 index 000000000..fc60d4db0 --- /dev/null +++ b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/KotlinStyleJpqlPropertyIntrospector.kt @@ -0,0 +1,33 @@ +package com.linecorp.kotlinjdsl.render.jpql.introspector.impl + +import com.linecorp.kotlinjdsl.Internal +import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlPropertyDescription +import com.linecorp.kotlinjdsl.render.jpql.introspector.JpqlPropertyIntrospector +import kotlin.reflect.KCallable +import kotlin.reflect.KFunction1 +import kotlin.reflect.KProperty1 + +/** + * Introspector that introspects a property name in KCallable using Kotlin style. + */ +@Internal +class KotlinStyleJpqlPropertyIntrospector : JpqlPropertyIntrospector() { + override fun introspect(property: KCallable<*>): JpqlPropertyDescription? { + return when (property) { + is KProperty1<*, *> -> KotlinStyleProperty(property.name) + is KFunction1<*, *> -> KotlinStyleProperty(resolvePropertyName(property)) + else -> null + } + } + + private fun resolvePropertyName(getter: KFunction1<*, *>): String = + if (getter.name.startsWith("is")) { + getter.name + } else { + getter.name.removePrefix("get").replaceFirstChar { it.lowercase() } + } +} + +private data class KotlinStyleProperty( + override val name: String, +) : JpqlPropertyDescription diff --git a/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JakartaJpqlIntrospectorTest.kt b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JakartaJpqlIntrospectorTest.kt index 0b5962e58..743e544c0 100644 --- a/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JakartaJpqlIntrospectorTest.kt +++ b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JakartaJpqlIntrospectorTest.kt @@ -1,9 +1,7 @@ package com.linecorp.kotlinjdsl.render.jpql.introspector.impl -import io.mockk.mockkClass import org.assertj.core.api.WithAssertions import org.junit.jupiter.api.Test -import kotlin.reflect.KProperty0 class JakartaJpqlIntrospectorTest : WithAssertions { private val sut = JakartaJpqlIntrospector() @@ -13,7 +11,7 @@ class JakartaJpqlIntrospectorTest : WithAssertions { } @Test - fun `introspect(type) returns name of entity annotation, when entity annotation has name`() { + fun `introspect() returns name of entity annotation, when entity annotation has name`() { // given val type = EntityClass1::class @@ -25,7 +23,7 @@ class JakartaJpqlIntrospectorTest : WithAssertions { } @Test - fun `introspect(type) returns name of class, when entity annotation does not have name`() { + fun `introspect() returns name of class, when entity annotation does not have name`() { // given val type = EntityClass2::class @@ -37,7 +35,7 @@ class JakartaJpqlIntrospectorTest : WithAssertions { } @Test - fun `introspect(type) returns null, when there is no entity annotation`() { + fun `introspect() returns null, when there is no entity annotation`() { // given val type = NonEntityCLass1::class @@ -48,76 +46,8 @@ class JakartaJpqlIntrospectorTest : WithAssertions { assertThat(actual?.name).isNull() } - @Test - fun `introspect(property) returns property name, when property is KProperty1`() { - // given - val property = EntityClass1::property1 - - // when - val actual = sut.introspect(property) - - // then - assertThat(actual?.name).isEqualTo("property1") - } - - @Test - fun `introspect(property) returns property name with prefix removed, when getter name starts with get`() { - // given - val property = EntityClass1::getProperty2 - - // when - val actual = sut.introspect(property) - - // then - assertThat(actual?.name).isEqualTo("property2") - } - - @Test - fun `introspect(property) returns property name as is, when getter name starts with is`() { - // given - val property = EntityClass1::isProperty3 - - // when - val actual = sut.introspect(property) - - // then - assertThat(actual?.name).isEqualTo("isProperty3") - } - - @Test - fun `introspect(property) returns property name as is, when getter name does not start with get or is`() { - // given - val property = EntityClass1::someProperty - - // when - val actual = sut.introspect(property) - - // then - assertThat(actual?.name).isEqualTo("someProperty") - } - - @Test - fun `introspect(property) returns null, when property is not KProperty1 or KFunction1`() { - // given - val property = mockkClass(KProperty0::class) - - // when - val actual = sut.introspect(property) - - // then - assertThat(actual?.name).isNull() - } - @jakarta.persistence.Entity(name = ENTITY_NAME_1) - private open class EntityClass1 { - val property1: Long = 0 - - fun getProperty2(): Long = 100 - - fun isProperty3(): Boolean = true - - fun someProperty(): String = "someProperty" - } + private open class EntityClass1 @jakarta.persistence.Entity private open class EntityClass2 diff --git a/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JavaxJpqlIntrospectorTest.kt b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JavaxJpqlIntrospectorTest.kt index f8d70a923..4b161c54d 100644 --- a/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JavaxJpqlIntrospectorTest.kt +++ b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/JavaxJpqlIntrospectorTest.kt @@ -1,9 +1,7 @@ package com.linecorp.kotlinjdsl.render.jpql.introspector.impl -import io.mockk.mockkClass import org.assertj.core.api.WithAssertions import org.junit.jupiter.api.Test -import kotlin.reflect.KProperty0 class JavaxJpqlIntrospectorTest : WithAssertions { private val sut = JavaxJpqlIntrospector() @@ -13,7 +11,7 @@ class JavaxJpqlIntrospectorTest : WithAssertions { } @Test - fun `introspect(type) returns name of entity annotation, when entity annotation has name`() { + fun `introspect() returns name of entity annotation, when entity annotation has name`() { // given val type = EntityClass1::class @@ -48,76 +46,8 @@ class JavaxJpqlIntrospectorTest : WithAssertions { assertThat(actual?.name).isNull() } - @Test - fun `introspect(property) returns property name, when property is KProperty1`() { - // given - val property = EntityClass1::property1 - - // when - val actual = sut.introspect(property) - - // then - assertThat(actual?.name).isEqualTo("property1") - } - - @Test - fun `introspect(property) returns property name with prefix removed, when getter name starts with get`() { - // given - val property = EntityClass1::getProperty2 - - // when - val actual = sut.introspect(property) - - // then - assertThat(actual?.name).isEqualTo("property2") - } - - @Test - fun `introspect(property) returns property name as is, when getter name starts with is`() { - // given - val property = EntityClass1::isProperty3 - - // when - val actual = sut.introspect(property) - - // then - assertThat(actual?.name).isEqualTo("isProperty3") - } - - @Test - fun `introspect(property) returns property name as is, when getter name does not start with get or is`() { - // given - val property = EntityClass1::someProperty - - // when - val actual = sut.introspect(property) - - // then - assertThat(actual?.name).isEqualTo("someProperty") - } - - @Test - fun `introspect(property) returns null, when property is not KProperty1 or KFunction1`() { - // given - val property = mockkClass(KProperty0::class) - - // when - val actual = sut.introspect(property) - - // then - assertThat(actual?.name).isNull() - } - @javax.persistence.Entity(name = ENTITY_NAME_1) - private open class EntityClass1 { - val property1: Long = 0 - - fun getProperty2(): Long = 100 - - fun isProperty3(): Boolean = true - - fun someProperty(): String = "someProperty" - } + private open class EntityClass1 @javax.persistence.Entity private open class EntityClass2 diff --git a/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/KotlinStyleJpqlPropertyIntrospectorTest.kt b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/KotlinStyleJpqlPropertyIntrospectorTest.kt new file mode 100644 index 000000000..e5bff4570 --- /dev/null +++ b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/introspector/impl/KotlinStyleJpqlPropertyIntrospectorTest.kt @@ -0,0 +1,80 @@ +package com.linecorp.kotlinjdsl.render.jpql.introspector.impl + +import io.mockk.mockkClass +import org.assertj.core.api.WithAssertions +import org.junit.jupiter.api.Test +import kotlin.reflect.KProperty0 + +class KotlinStyleJpqlPropertyIntrospectorTest : WithAssertions { + private val sut = KotlinStyleJpqlPropertyIntrospector() + + @Test + fun `introspect() returns property name, when property is KProperty1`() { + // given + val property = EntityClass1::property1 + + // when + val actual = sut.introspect(property) + + // then + assertThat(actual?.name).isEqualTo("property1") + } + + @Test + fun `introspect() returns property name with prefix removed, when getter name starts with get`() { + // given + val property = EntityClass1::getProperty2 + + // when + val actual = sut.introspect(property) + + // then + assertThat(actual?.name).isEqualTo("property2") + } + + @Test + fun `introspect() returns property name as is, when getter name starts with is`() { + // given + val property = EntityClass1::isProperty3 + + // when + val actual = sut.introspect(property) + + // then + assertThat(actual?.name).isEqualTo("isProperty3") + } + + @Test + fun `introspect() returns property name as is, when getter name does not start with get or is`() { + // given + val property = EntityClass1::someProperty + + // when + val actual = sut.introspect(property) + + // then + assertThat(actual?.name).isEqualTo("someProperty") + } + + @Test + fun `introspect() returns null, when property is not KProperty1 or KFunction1`() { + // given + val property = mockkClass(KProperty0::class) + + // when + val actual = sut.introspect(property) + + // then + assertThat(actual?.name).isNull() + } + + private class EntityClass1 { + val property1: Long = 0 + + fun getProperty2(): Long = 100 + + fun isProperty3(): Boolean = true + + fun someProperty(): String = "someProperty" + } +}