0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【jOOQ】PostgreSQLでTIMESTAMP WITH TIME ZONEからOffsetDateTimeへの読み出しがCannot decode value of type java.lang.String with OID 1184で失敗する問題への対処【R2DBC】

Last updated at Posted at 2022-02-28

TL;DR

  • Settings.isBindOffsetDateTimeTypefalseになっていた場合、jOOQR2DBCから文字列で情報を読み出し、OffsetDateTimeへパースしようとする
  • 一方、r2dbc-postgresqlTIMESTAMP WITH TIME ZONEを文字列へデコードする手段を提供していないため、読み出しが失敗する
  • 読み出し処理時にSettings.isBindOffsetDateTimeTypetrueになるよう設定することでこの問題は解決する

状況

Spring Boot + R2DBC + jOOQのプロジェクトにて、H2からPostgresへの移行を行なっていた所、OffsetDateTimeを読み出す処理が何も変更していないのに失敗するようになりました。
問題となった部分のスタックトレースは以下のようになっていました。

スタックトレース(Causedより上は省略)
Caused by: java.lang.IllegalArgumentException: Cannot decode value of type java.lang.String with OID 1184
	at io.r2dbc.postgresql.codec.DefaultCodecs.decode(DefaultCodecs.java:158) ~[r2dbc-postgresql-0.8.8.RELEASE.jar:0.8.8.RELEASE]
	at io.r2dbc.postgresql.PostgresqlRow.decode(PostgresqlRow.java:90) ~[r2dbc-postgresql-0.8.8.RELEASE.jar:0.8.8.RELEASE]
	at io.r2dbc.postgresql.PostgresqlRow.get(PostgresqlRow.java:67) ~[r2dbc-postgresql-0.8.8.RELEASE.jar:0.8.8.RELEASE]
	at org.jooq.impl.R2DBC$R2DBCResultSet$DefaultRow.get(R2DBC.java:1110) ~[jooq-3.15.1.jar:na]
	at org.jooq.impl.R2DBC$R2DBCResultSet.nullable(R2DBC.java:989) ~[jooq-3.15.1.jar:na]
	at org.jooq.impl.R2DBC$R2DBCResultSet.nullable(R2DBC.java:985) ~[jooq-3.15.1.jar:na]
	at org.jooq.impl.R2DBC$R2DBCResultSet.getString(R2DBC.java:1050) ~[jooq-3.15.1.jar:na]
	at org.jooq.impl.DefaultBinding$DefaultOffsetDateTimeBinding.get0(DefaultBinding.java:3085) ~[jooq-3.15.1.jar:na]
	at org.jooq.impl.DefaultBinding$DefaultOffsetDateTimeBinding.get0(DefaultBinding.java:3000) ~[jooq-3.15.1.jar:na]
	at org.jooq.impl.DefaultBinding$AbstractBinding.get(DefaultBinding.java:942) ~[jooq-3.15.1.jar:na]
	at org.jooq.impl.R2DBC$ResultSubscriber.lambda$onNext$0(R2DBC.java:327) ~[jooq-3.15.1.jar:na]
	at org.jooq.impl.RecordDelegate.operate(RecordDelegate.java:143) ~[jooq-3.15.1.jar:na]
	at org.jooq.impl.R2DBC$ResultSubscriber.lambda$onNext$1(R2DBC.java:313) ~[jooq-3.15.1.jar:na]
	at io.r2dbc.postgresql.PostgresqlResult.lambda$map$1(PostgresqlResult.java:111) ~[r2dbc-postgresql-0.8.8.RELEASE.jar:0.8.8.RELEASE]

ここで、OID 1184io.r2dbc.postgresql.type.PostgresqlObjectId.TIMESTAMPTZ(= TIMESTAMP WITH TIME ZONE)を表します。
従って、このエラーメッセージは「TIMESTAMP WITH TIME ZONEjava.lang.Stringとして読み出せなかった」ということを意味します。

原因

コードを追いかけた所、org.jooq.impl.DefaultBinding.DefaultOffsetDateTimeBinding.get0の関数がgetStringで情報を読み出していることが直接の原因でした。
実際のコードは以下の通りです。

jOOQのソースより抜粋
@Override
final OffsetDateTime get0(BindingGetResultSetContext<U> ctx) throws SQLException {
  if (!FALSE.equals(ctx.settings().isBindOffsetDateTimeType()))
    return ctx.resultSet().getObject(ctx.index(), OffsetDateTime.class);
  else
    return OffsetDateTimeParser.offsetDateTime(ctx.resultSet().getString(ctx.index()));
}

対処

先ほどお見せしたコードで、getObjectが呼ばれるようにする、つまりSettings.isBindOffsetDateTimeTypetrueをセットすることで対処できました。
具体的な設定方法は幾つか考えられますが、自分はBean初期化時に以下のようにすることで対処しました。

import io.r2dbc.spi.ConnectionFactory
import org.jooq.DSLContext
import org.jooq.SQLDialect
import org.jooq.conf.Settings
import org.jooq.impl.DSL
import org.springframework.context.annotation.Bean
import org.springframework.stereotype.Component

@Component
class Config(val cfi: ConnectionFactory) {
    @Bean
    fun jooqDSLContext(): DSLContext = DSL.using(
        cfi,
        SQLDialect.POSTGRES,
        Settings().apply { isBindOffsetDateTimeType = true }
    ).dsl()
}

この設定方法に関しては自信が無いため、より良い方法などあればご教示頂けると幸いです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?