Closed rsmidt closed 8 months ago
You need to apply casting on the SQL level. Alternatively, you can provide a converter from MyTsRangeType
to Parameter
if you can provide the Postgres Type OID.
The driver doesn't provide any value objects for ranges, therefore you must bind the value as string and tell the driver to use tsrange
R2DBC's Parameters.in(…)
along with the driver's PostgresTypes.lookupType("tsrange")
should return you the type descriptor required for Parameters.in(myTsRangeType.toString(), typeDesc)
.
Let me know whether this helps.
Hey @mp911de, thanks for your swift reply.
Ideally, I could avoid doing it on the SQL level and fully rely on the abstractions already there. Following your feedback, I tried the following (excuse the Kotlin):
// Taken from PostgresTypes.from(...).lookupType("tsrange")
private val PG_RANGE_TYPE = PostgresTypes.PostgresType(3908, 3908, 3909, 3909, "tsrange", "R")
@WritingConverter
object TsRangeSerializer : Converter<TsRange, Parameter> {
override fun convert(source: TsRange): Parameter {
return Parameters.`in`(PG_RANGE_TYPE, source.toPostgresValue())
}
}
fun TsRange.toPostgresValue(): String =
"[\"${start.format(PG_TIMESTAMP_FORMATTER)}\",\"${end.format(PG_TIMESTAMP_FORMATTER)}\"]"
Unfortunately, now it fails with an error I don't fully comprehend:
java.lang.IllegalArgumentException: Cannot encode null parameter of type io.r2dbc.spi.Parameters$InParameter at io.r2dbc.postgresql.codec.DefaultCodecs.encodeNull(DefaultCodecs.java:302)
I don't understand why it tries to bind to null
?
null
values are not using the Converter
, instead, we use the property (converted) type and not a value.
You could register a BeforeSaveCallback
and post-process the OutboundRow
to replace the value with a proper Parameters.in(…)
null value wrapped in a Spring org.springframework.r2dbc.core.Parameter
.
Maybe I'm misunderstanding something, but the value is not null in this case. The column itself is actually not nullable. That's why I expect it will never try to bind anything nullish.
Can you provide the full stack trace and a minimal sample (SQL, domain types, converters, test)? Happy to debug the issue here once I have a bit of code that gets me started.
Thank you! Here's the repo: https://github.com/rsmidt/spring-data-r2dbc-range-types
It's written in Kotlin. I can change it to Java if required. It's expecting Postgres running on port 5432, configured in the application.properties (there's also a docker-compose.yml).
Please let me know if I can help in any way.
I moved this ticket into Spring Data Relational as Spring Data R2DBC is part of the Relational project.
This is a bug where we attempt an entity conversion by extracting the identifier.
That's fixed now. Care to upgrade to 3.2.1-SNAPSHOT
of spring-data-r2dbc
and retest whether the fix addresses your problem?
Stupid question, but where is the snapshot being published? If I try to override it like this:
implementation("org.springframework.boot:spring-boot-starter-data-r2dbc")
implementation("org.springframework.data:spring-data-r2dbc:3.2.1-SNAPSHOT")
It fails:
Could not find org.springframework.data:spring-data-r2dbc:3.2.1-SNAPSHOT. Required by: project : Could not find org.springframework.data:spring-data-r2dbc:3.2.1-SNAPSHOT. Required by: project : > org.springframework.boot:spring-boot-starter-data-r2dbc:3.2.0
No worries, the repo is at https://repo.spring.io/snapshot
. The Maven declaration would be:
<repository>
<id>spring-snapshot</id>
<name>Spring Snapshot Repository</name>
<url>https://repo.spring.io/snapshot</url>
</repository>
Yes! It's working now directly using the entity template.
Thank you very much for the swift fix, it's much appreciated.
I'm currently struggling to use a simple
tsrange
in a template entity insert.Reading is fine, as I can utilize a simple
Converter<String, MyTsRangeType>
. But doing the same for writing (Converter<MyTsRangeType, String>
) does not work:Is there a proper way for dealing with ranges?