r2dbc/r2dbc-h2

Cannot save `org.locationtech.jts.geom.Geometry` into H2 database

yandroidUA opened this issue · 2 comments

Bug Report

Versions

  • Driver: 1.0.0.RELEASE
  • Database: H2 (provided by Spring Boot) - 2.2 (accordingly to Github release)
  • Kotlin: 1.9.20
  • OS: MacOS 14.3.1
  • Spring Boot: 3.2.2;
  • io.r2dbc.r2dbc-h2:1.0.0.RELEASE;
  • io.r2dbc.r2dbc-spi:1.0.0.RELEASE;
  • org.springframework.boot:spring-boot-starter-data-r2dbc: (provided by Spring Boot);
  • org.springframework.boot:spring-boot-starter-data-webflux: (provided by Spring Boot);
  • org.locationtech.jts:jts-core: 1.16.0;

P.S. I'm using Liquebase to create initial schema to perform migrations.

Current Behavior

I'm trying to save an entity in database, but keep getting an error.

data class GeometryToXYZEntity @JvmOverloads constructor(
    @Id
    @Column("uuid")
    val uuid: UUID? = null,
    @Column("geometry")
    val geometry: Geometry? = null,
    @Column("xyz_uuid")
    val xyzUuid: UUID? = null,
)
Stack trace
org.springframework.dao.InvalidDataAccessApiUsageException: Nested entities are not supported
	at org.springframework.data.r2dbc.convert.MappingR2dbcConverter.writePropertyInternal(MappingR2dbcConverter.java:251) ~[spring-data-r2dbc-3.2.2.jar:3.2.2]
	at org.springframework.data.r2dbc.convert.MappingR2dbcConverter.writeProperties(MappingR2dbcConverter.java:218) ~[spring-data-r2dbc-3.2.2.jar:3.2.2]
	at org.springframework.data.r2dbc.convert.MappingR2dbcConverter.writeInternal(MappingR2dbcConverter.java:189) ~[spring-data-r2dbc-3.2.2.jar:3.2.2]
	at org.springframework.data.r2dbc.convert.MappingR2dbcConverter.write(MappingR2dbcConverter.java:181) ~[spring-data-r2dbc-3.2.2.jar:3.2.2]
	at org.springframework.data.r2dbc.convert.MappingR2dbcConverter.write(MappingR2dbcConverter.java:61) ~[spring-data-r2dbc-3.2.2.jar:3.2.2]
	at org.springframework.data.r2dbc.core.DefaultReactiveDataAccessStrategy.getOutboundRow(DefaultReactiveDataAccessStrategy.java:177) ~[spring-data-r2dbc-3.2.2.jar:3.2.2]
	at org.springframework.data.r2dbc.core.R2dbcEntityTemplate.lambda$doInsert$6(R2dbcEntityTemplate.java:480) ~[spring-data-r2dbc-3.2.2.jar:3.2.2]
	at reactor.core.publisher.FluxFlatMap.trySubscribeScalarMap(FluxFlatMap.java:153) ~[reactor-core-3.6.2.jar:3.6.2]
	at reactor.core.publisher.MonoFlatMap.subscribeOrReturn(MonoFlatMap.java:53) ~[reactor-core-3.6.2.jar:3.6.2]
	at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:63) ~[reactor-core-3.6.2.jar:3.6.2]
	at reactor.core.publisher.MonoUsingWhen.subscribe(MonoUsingWhen.java:87) ~[reactor-core-3.6.2.jar:3.6.2]
	at reactor.core.publisher.Mono.subscribe(Mono.java:4512) ~[reactor-core-3.6.2.jar:3.6.2]
	at kotlinx.coroutines.reactor.MonoKt.awaitSingleOrNull(Mono.kt:47) ~[kotlinx-coroutines-reactor-1.6.4.jar:na]
	at org.springframework.aop.framework.CoroutinesUtils.awaitSingleOrNull(CoroutinesUtils.java:42) ~[spring-aop-6.1.3.jar:6.1.3]
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:239) ~[spring-aop-6.1.3.jar:6.1.3]
	at jdk.proxy2/jdk.proxy2.$Proxy117.save(Unknown Source) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
	at kotlin.reflect.jvm.internal.calls.CallerImpl$Method.callMethod(CallerImpl.kt:97) ~[kotlin-reflect-1.9.20.jar:1.9.255-SNAPSHOT]
	at kotlin.reflect.jvm.internal.calls.CallerImpl$Method$Instance.call(CallerImpl.kt:113) ~[kotlin-reflect-1.9.20.jar:1.9.255-SNAPSHOT]
	at kotlin.reflect.jvm.internal.KCallableImpl.callDefaultMethod$kotlin_reflection(KCallableImpl.kt:207) ~[kotlin-reflect-1.9.20.jar:1.9.255-SNAPSHOT]
	at kotlin.reflect.full.KCallables.callSuspendBy(KCallables.kt:74) ~[kotlin-reflect-1.9.20.jar:1.9.255-SNAPSHOT]
	at org.springframework.core.CoroutinesUtils.lambda$invokeSuspendingFunction$3(CoroutinesUtils.java:137) ~[spring-core-6.1.3.jar:6.1.3]
	at kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt$createCoroutineUnintercepted$$inlined$createCoroutineFromSuspendFunction$IntrinsicsKt__IntrinsicsJvmKt$4.invokeSuspend(IntrinsicsJvm.kt:270) ~[kotlin-stdlib-1.9.20.jar:1.9.255-SNAPSHOT]
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) ~[kotlin-stdlib-1.9.20.jar:1.9.255-SNAPSHOT]
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
	at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:280) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
	at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
	at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
	at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
	at com.app.animalcare.server.initialize.DevCreateAnimalPostCommand.run(DevCreateAnimalPostCommand.kt:29) ~[main/:na]
	at org.springframework.boot.SpringApplication.lambda$callRunner$5(SpringApplication.java:790) ~[spring-boot-3.2.2.jar:3.2.2]
	at org.springframework.util.function.ThrowingConsumer$1.acceptWithException(ThrowingConsumer.java:83) ~[spring-core-6.1.3.jar:6.1.3]
	at org.springframework.util.function.ThrowingConsumer.accept(ThrowingConsumer.java:60) ~[spring-core-6.1.3.jar:6.1.3]
	at org.springframework.util.function.ThrowingConsumer$1.accept(ThrowingConsumer.java:88) ~[spring-core-6.1.3.jar:6.1.3]
	at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:798) ~[spring-boot-3.2.2.jar:3.2.2]
	at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:789) ~[spring-boot-3.2.2.jar:3.2.2]
	at org.springframework.boot.SpringApplication.lambda$callRunners$3(SpringApplication.java:774) ~[spring-boot-3.2.2.jar:3.2.2]
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183) ~[na:na]
	at java.base/java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:357) ~[na:na]
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:510) ~[na:na]
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[na:na]
	at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150) ~[na:na]
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173) ~[na:na]
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]
	at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) ~[na:na]
	at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:774) ~[spring-boot-3.2.2.jar:3.2.2]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:341) ~[spring-boot-3.2.2.jar:3.2.2]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1354) ~[spring-boot-3.2.2.jar:3.2.2]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343) ~[spring-boot-3.2.2.jar:3.2.2]
	at com.app.animalcare.server.MainKt.main(Main.kt:26) ~[main/:na]

Table schema

Input Code
CREATE TABLE PUBLIC.geometry_to_animal_advertisement_entity (uuid UUID DEFAULT random_uuid() NOT NULL, geometry GEOMETRY NOT NULL, advertisement_uuid VARCHAR(36) NOT NULL, CONSTRAINT PK_GEOMETRY_TO_ANIMAL_ADVERTISEMENT_ENTITY PRIMARY KEY (uuid), UNIQUE (uuid))

Will be very appreciate to receive a piece of advice or any help, thanks!

This is a Spring Data issue, please file the issue at https://github.com/spring-projects/spring-data-relational/issues so we can add Geometry as simple type to H2Dialect. As workaround, add Geometry to H2Dialect.simpleTypes() in a custom subclass.