Skip to content

Commit 1330136

Browse files
authored
Support spatial types for KSP (#1386)
KSP will correctly process spatial types in entities. Solves #1375
2 parents cfc0ecc + 71ae59b commit 1330136

File tree

5 files changed

+112
-11
lines changed

5 files changed

+112
-11
lines changed

querydsl-examples/querydsl-example-ksp-codegen/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ repositories {
2020
dependencies {
2121
implementation("jakarta.persistence:jakarta.persistence-api:${jpaVersion}")
2222
implementation("io.github.openfeign.querydsl:querydsl-core:${querydslVersion}")
23+
implementation("io.github.openfeign.querydsl:querydsl-spatial:${querydslVersion}")
2324
implementation("org.hibernate.orm:hibernate-core:${hibernateVersion}")
2425
ksp("io.github.openfeign.querydsl:querydsl-ksp-codegen:${querydslVersion}")
2526
implementation("org.locationtech.jts:jts-core:1.19.0")
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.querydsl.example.ksp
2+
3+
import jakarta.persistence.Entity
4+
import jakarta.persistence.GeneratedValue
5+
import jakarta.persistence.GenerationType
6+
import jakarta.persistence.Id
7+
import org.locationtech.jts.geom.Geometry
8+
9+
@Entity
10+
class MyShape(
11+
@Id
12+
@GeneratedValue(strategy = GenerationType.IDENTITY)
13+
val id: Long = 0,
14+
val departureGeo: Geometry? = null
15+
)

querydsl-examples/querydsl-example-ksp-codegen/src/test/kotlin/Tests.kt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import com.querydsl.example.ksp.QBearSimplifiedProjection
88
import com.querydsl.example.ksp.QCat
99
import com.querydsl.example.ksp.QDog
1010
import com.querydsl.example.ksp.QGeolocation
11+
import com.querydsl.example.ksp.QMyShape
1112
import com.querydsl.example.ksp.QPerson
1213
import com.querydsl.example.ksp.QPersonClassDTO
1314
import com.querydsl.example.ksp.QPersonClassConstructorDTO
@@ -19,7 +20,9 @@ import org.assertj.core.api.Assertions.assertThat
1920
import org.assertj.core.api.Assertions.fail
2021
import org.hibernate.cfg.Configuration
2122
import kotlin.reflect.full.declaredMemberProperties
23+
import kotlin.reflect.full.memberProperties
2224
import kotlin.reflect.full.primaryConstructor
25+
import kotlin.reflect.jvm.jvmErasure
2326
import kotlin.test.Test
2427

2528
class Tests {
@@ -249,15 +252,12 @@ class Tests {
249252
}
250253
}
251254

252-
@Test
253-
fun `is detecting comparable interface on unknown type`() {
254-
val locationTypeName = QGeolocation::class
255-
.members
256-
.first { it.name == "location" }
257-
.returnType
258-
.toString()
259-
assertThat(locationTypeName).isEqualTo("com.querydsl.core.types.dsl.ComparablePath<org.locationtech.jts.geom.Point>")
260-
}
255+
@Test
256+
fun ensureCorrectGeoType() {
257+
val departureProperty = QMyShape::class.memberProperties.single { it.name == "departureGeo" }
258+
assertThat(departureProperty.returnType.jvmErasure.qualifiedName!!).isEqualTo("com.querydsl.spatial.locationtech.jts.JTSGeometryPath")
259+
assertThat(departureProperty.returnType.arguments.single().type!!.jvmErasure.qualifiedName!!).isEqualTo("org.locationtech.jts.geom.Geometry")
260+
}
261261

262262
private fun initialize(): EntityManagerFactory {
263263
val configuration = Configuration()

querydsl-tooling/querydsl-ksp-codegen/src/main/kotlin/com/querydsl/ksp/codegen/SimpleType.kt

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,21 @@ sealed interface SimpleType {
165165
}
166166
}
167167

168+
class Custom(
169+
override val className: ClassName,
170+
override val pathClassName: ClassName,
171+
override val pathTypeName: TypeName
172+
) : SimpleType {
173+
override fun render(name: String): PropertySpec {
174+
return PropertySpec
175+
.builder(name, pathTypeName)
176+
.initializer("${pathClassName}(forProperty(\"$name\"))")
177+
.build()
178+
}
179+
}
180+
168181
object Mapper {
169-
private val typeMap: Map<ClassName, SimpleType> = mutableMapOf<KClass<*>, SimpleType>()
182+
private val simpleTypeMap: Map<ClassName, SimpleType> = mutableMapOf<KClass<*>, SimpleType>()
170183
.apply {
171184
this[Any::class] = Simple(Any::class.asClassName())
172185
this[Char::class] = Comparable(Char::class.asClassName())
@@ -215,8 +228,54 @@ sealed interface SimpleType {
215228
}
216229
.mapKeys { it.key.asClassName() }
217230

231+
private val customTypeMap: Map<ClassName, SimpleType> = mutableMapOf<ClassName, SimpleType>()
232+
.apply {
233+
val mapping = mutableMapOf<ClassName, ClassName>()
234+
apply {
235+
val map = mapOf(
236+
"Geometry" to "GeometryPath",
237+
"GeometryCollection" to "GeometryCollectionPath",
238+
"LinearRing" to "LinearRingPath",
239+
"LineString" to "LineStringPath",
240+
"MultiLineString" to "MultiLineStringPath",
241+
"MultiPoint" to "MultiPointPath",
242+
"MultiPolygon" to "MultiPolygonPath",
243+
"Point" to "PointPath",
244+
"Polygon" to "PolygonPath"
245+
)
246+
for (entry in map) {
247+
mapping[ClassName("org.geolatte.geom", entry.key)] = ClassName("com.querydsl.spatial", entry.value)
248+
}
249+
}
250+
251+
apply {
252+
val map = mapOf(
253+
"Geometry" to "JTSGeometryPath",
254+
"GeometryCollection" to "JTSGeometryCollectionPath",
255+
"LinearRing" to "JTSLinearRingPath",
256+
"LineString" to "JTSLineStringPath",
257+
"MultiLineString" to "JTSMultiLineStringPath",
258+
"MultiPoint" to "JTSMultiPointPath",
259+
"MultiPolygon" to "JTSMultiPolygonPath",
260+
"Point" to "JTSPointPath",
261+
"Polygon" to "JTSPolygonPath",
262+
)
263+
for (entry in map) {
264+
mapping[ClassName("com.vividsolutions.jts.geom", entry.key)] = ClassName("com.querydsl.spatial.jts", entry.value)
265+
mapping[ClassName("org.locationtech.jts.geom", entry.key)] = ClassName("com.querydsl.spatial.locationtech.jts", entry.value)
266+
}
267+
}
268+
for (entry in mapping) {
269+
this[entry.key] = Custom(
270+
entry.key,
271+
entry.value,
272+
entry.value.parameterizedBy(entry.key)
273+
)
274+
}
275+
}
276+
218277
fun get(className: ClassName): SimpleType? {
219-
return typeMap[className]
278+
return simpleTypeMap[className] ?: customTypeMap[className]
220279
}
221280
}
222281
}

querydsl-tooling/querydsl-ksp-codegen/src/test/kotlin/RenderTest.kt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,32 @@ class RenderTest {
253253
code.assertContains("class QLevel1_Level2_Level3")
254254
code.assertContains("EntityPathBase<Level1.Level2.Level3>")
255255
}
256+
257+
@Test
258+
fun geometryTypeRendering() {
259+
val model = QueryModel(
260+
originalClassName = ClassName("", "MyShape"),
261+
typeParameterCount = 0,
262+
className = ClassName("", "QMyShape"),
263+
type = QueryModelType.ENTITY,
264+
null,
265+
mockk()
266+
)
267+
val properties = listOf(
268+
QProperty(
269+
"geometry",
270+
QPropertyType.Simple(
271+
SimpleType.Mapper.get(ClassName("org.locationtech.jts.geom", "Geometry"))!!
272+
)
273+
)
274+
)
275+
model.properties.addAll(properties)
276+
val typeSpec = QueryModelRenderer.render(model)
277+
val code = typeSpec.toString()
278+
code.assertContains("""
279+
public val geometry: com.querydsl.spatial.locationtech.jts.JTSGeometryPath<org.locationtech.jts.geom.Geometry> = com.querydsl.spatial.locationtech.jts.JTSGeometryPath(forProperty("geometry"))
280+
""".trimIndent())
281+
}
256282
}
257283

258284
private fun String.assertLines(expected: String) {

0 commit comments

Comments
 (0)