如何使用 jOOQ gradle 插件将 bigint[] 字段从 postgres 转换为 class 字段
How to convert bigint[] field from postgres into class field with a jOOQ gradle plugin
所有感兴趣的人早上好 :) 我对 jOOQ 有一些疑问。
我需要从 PostgreSQL table 生成 jOOQ 实体。它有一个 bigint[]
类型的字段。 jOOQ Gradle 插件(我正在使用这个 https://github.com/etiennestuder/gradle-jooq-plugin)不支持数组 DateType,所以唯一的方法是使用带有强制类型的自定义类型
// jooq config in build.gradle
customTypes {
customType {
name = "BigintArrayBinder"
type = "Long[]"
converter = "ru.stdev.tskad.yandexnavigator.binder.BigintArrayBinder"
}
}
forcedTypes {
forcedType {
name = "BigintArrayBinder"
includeExpression = '.*'
includeTypes = 'ARRAY'
}
}
这是这个活页夹及其转换器的实现
public class BigintArrayConverter implements Converter<Object[], Long[]> {
@Override
public Long[] from(Object[] t) {
return t == null ? new Long[]{} : (Long[]) t;
}
@Override
public Object[] to(Long[] u) {
return u == null || u.length == 0 ? new Object[]{} : u;
}
@Override
public Class<Object[]> fromType() {
return Object[].class;
}
@Override
public Class<Long[]> toType() {
return Long[].class;
}
}
import org.jooq.*;
import org.jooq.conf.ParamType;
import org.jooq.impl.DSL;
import org.postgresql.core.BaseConnection;
import org.postgresql.jdbc.PgArray;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class BigintArrayBinder implements Binding<Object[], Long[]> {
// The converter does all the work
@Override
public Converter<Object[], Long[]> converter() {
return new BigintArrayConverter();
}
// Rending a bind variable for the binding context's value and casting it to the json type
@Override
public void sql(BindingSQLContext<Long[]> ctx) throws SQLException {
// Depending on how you generate your SQL, you may need to explicitly distinguish
// between jOOQ generating bind variables or inlined literals.
if (ctx.render().paramType() == ParamType.INLINED)
ctx.render().visit(DSL.inline(ctx.convert(converter()).value())).sql("::bigint[]");
else
ctx.render().sql("::bigint[]");
}
// Registering ARRAY types for JDBC CallableStatement OUT parameters
@Override
public void register(BindingRegisterContext<Long[]> ctx) throws SQLException {
ctx.statement().registerOutParameter(ctx.index(), Types.ARRAY);
}
// Converting the Long[] to a bigint[] value and setting that on a JDBC PreparedStatement
@Override
public void set(BindingSetStatementContext<Long[]> ctx) throws SQLException {
Object[] value = ctx.convert(converter()).value();
BaseConnection connection = (BaseConnection) DriverManager.getConnection(
"jdbc:postgresql://localhost:5432/m4",
"postgres",
"postgres"
);
int longOid = 20;
System.out.print("value -> ");
System.out.println(Arrays.toString(value));
PgArray pgArray = new PgArray(connection, longOid, Arrays.toString(value));
ctx.statement().setArray(ctx.index(), pgArray);
}
// Getting a bigint[] value from a JDBC ResultSet and converting that to a Long[]
@Override
public void get(BindingGetResultSetContext<Long[]> ctx) throws SQLException {
Object[] array = (Object[]) ctx.resultSet().getArray(ctx.index()).getArray();
ctx.convert(converter()).value(array);
}
// Getting a bigint[] value from a JDBC CallableStatement and converting that to a Long[]
@Override
public void get(BindingGetStatementContext<Long[]> ctx) throws SQLException {
Object[] array = (Object[]) ctx.statement().getArray(ctx.index()).getArray();
ctx.convert(converter()).value(array);
}
// Setting a value on a JDBC SQLOutput (useful for Oracle OBJECT types)
@Override
public void set(BindingSetSQLOutputContext<Long[]> ctx) throws SQLException {
throw new SQLFeatureNotSupportedException();
}
// Getting a value from a JDBC SQLInput (useful for Oracle OBJECT types)
@Override
public void get(BindingGetSQLInputContext<Long[]> ctx) throws SQLException {
throw new SQLFeatureNotSupportedException();
}
}
因此,它正在编译并且可以很好地用于选择查询。但是如果我尝试插入我有这个例外
org.jooq.exception.DataAccessException: SQL [null]; Error while writing value at JDBC bind index: 7
at org.jooq_3.13.1.POSTGRES.debug(Unknown Source)
at org.jooq.impl.Tools.translate(Tools.java:2751)
at org.jooq.impl.AbstractBindContext.bindValue(AbstractBindContext.java:127)
at org.jooq.impl.Val.accept(Val.java:103)
at org.jooq.impl.AbstractBindContext.bindInternal(AbstractBindContext.java:269)
at org.jooq.impl.AbstractBindContext.visit0(AbstractBindContext.java:88)
at org.jooq.impl.AbstractContext.visit0(AbstractContext.java:457)
at org.jooq.impl.AbstractContext.visit(AbstractContext.java:218)
at org.jooq.impl.QueryPartList.accept(QueryPartList.java:121)
at org.jooq.impl.AbstractBindContext.bindInternal(AbstractBindContext.java:269)
at org.jooq.impl.AbstractBindContext.visit0(AbstractBindContext.java:88)
at org.jooq.impl.AbstractContext.visit0(AbstractContext.java:457)
at org.jooq.impl.AbstractContext.visit(AbstractContext.java:218)
at org.jooq.impl.AbstractQuery.execute(AbstractQuery.java:367)
at org.jooq.impl.AbstractDelegatingQuery.execute(AbstractDelegatingQuery.java:119)
at ru.stdev.tskad.yandexnavigator.repository.NavigatorRepository$testInsert.invokeSuspend(NavigatorRepository.kt:37)
at ru.stdev.tskad.yandexnavigator.repository.NavigatorRepository$testInsert.invoke(NavigatorRepository.kt)
at ru.stdev.utp.common.jooq.DefaultJooqWrapper$launchOperation.invokeSuspend(DefaultJooqWrapper.kt:17)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:330)
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:26)
at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:109)
at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:158)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:58)
at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
at ru.stdev.utp.common.jooq.DefaultJooqWrapper.launchOperation(DefaultJooqWrapper.kt:16)
at ru.stdev.utp.common.jooq.DefaultJooqWrapper.request(DefaultJooqWrapper.kt:27)
at ru.stdev.tskad.yandexnavigator.repository.NavigatorRepository.testInsert(NavigatorRepository.kt:25)
at ru.stdev.tskad.yandexnavigator.routes.NavigatorRouteKt$navigator.invokeSuspend(NavigatorRoute.kt:23)
at ru.stdev.tskad.yandexnavigator.routes.NavigatorRouteKt$navigator.invoke(NavigatorRoute.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:318)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:163)
at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:183)
at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:27)
at io.ktor.routing.Routing.executeResult(Routing.kt:147)
at io.ktor.routing.Routing.interceptor(Routing.kt:34)
at io.ktor.routing.Routing$Feature$install.invokeSuspend(Routing.kt:99)
at io.ktor.routing.Routing$Feature$install.invoke(Routing.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:318)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:163)
at io.ktor.features.ContentNegotiation$Feature$install.invokeSuspend(ContentNegotiation.kt:107)
at io.ktor.features.ContentNegotiation$Feature$install.invoke(ContentNegotiation.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:318)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:163)
at io.ktor.features.StatusPages$interceptCall.invokeSuspend(StatusPages.kt:101)
at io.ktor.features.StatusPages$interceptCall.invoke(StatusPages.kt)
at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:91)
at kotlinx.coroutines.CoroutineScopeKt.coroutineScope(CoroutineScope.kt:194)
at io.ktor.features.StatusPages.interceptCall(StatusPages.kt:100)
at io.ktor.features.StatusPages$Feature$install.invokeSuspend(StatusPages.kt:140)
at io.ktor.features.StatusPages$Feature$install.invoke(StatusPages.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:318)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:163)
at io.ktor.features.CallLogging$Feature$install.invokeSuspend(CallLogging.kt:139)
at io.ktor.features.CallLogging$Feature$install.invoke(CallLogging.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:318)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:163)
at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:183)
at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:27)
at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline.invokeSuspend(DefaultEnginePipeline.kt:120)
at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline.invoke(DefaultEnginePipeline.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:318)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:163)
at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:183)
at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:27)
at io.ktor.server.netty.NettyApplicationCallHandler$handleRequest.invokeSuspend(NettyApplicationCallHandler.kt:40)
at io.ktor.server.netty.NettyApplicationCallHandler$handleRequest.invoke(NettyApplicationCallHandler.kt)
at kotlinx.coroutines.intrinsics.UndispatchedKt.startCoroutineUndispatched(Undispatched.kt:55)
at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:111)
at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:158)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:56)
at kotlinx.coroutines.BuildersKt.launch(Unknown Source)
at io.ktor.server.netty.NettyApplicationCallHandler.handleRequest(NettyApplicationCallHandler.kt:30)
at io.ktor.server.netty.NettyApplicationCallHandler.channelRead(NettyApplicationCallHandler.kt:24)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377)
at io.netty.channel.AbstractChannelHandlerContext.access0(AbstractChannelHandlerContext.java:59)
at io.netty.channel.AbstractChannelHandlerContext.run(AbstractChannelHandlerContext.java:368)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:164)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:472)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:500)
at io.netty.util.concurrent.SingleThreadEventExecutor.run(SingleThreadEventExecutor.java:989)
at io.netty.util.internal.ThreadExecutorMap.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:832)
Caused by: java.sql.SQLException: Error while writing value at JDBC bind index: 7
at org.jooq.impl.DefaultBindContext.bindValue0(DefaultBindContext.java:67)
at org.jooq.impl.AbstractBindContext.bindValue(AbstractBindContext.java:124)
... 81 common frames omitted
Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 63 out of bounds for length 63
at org.postgresql.jdbc.PgArray.buildArrayList(PgArray.java:450)
at org.postgresql.jdbc.PgArray.getBaseTypeName(PgArray.java:811)
at org.postgresql.jdbc.PgPreparedStatement.setArray(PgPreparedStatement.java:1098)
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.setArray(HikariProxyPreparedStatement.java)
at org.jooq.tools.jdbc.DefaultPreparedStatement.setArray(DefaultPreparedStatement.java:239)
at ru.stdev.tskad.yandexnavigator.binder.BigintArrayBinder.set(BigintArrayBinder.java:63)
at org.jooq.impl.DefaultBindContext.bindValue0(DefaultBindContext.java:62)
... 82 common frames omitted
首先让我感到困惑的是有必要创建 BaseConnection
。我可以从 DslContext 获得一个连接,但它 returns 一个 HikariProxyConnection
。因此,它不适用于 PgArray
,它需要 BaseConnection
。然后,我需要给它一个 oid
,等于 20 到 Long 类型。然后我必须给出一个数组作为字符串或字节数组。结果我有 ArrayIndexOutOfBoundsException
。我做了很多尝试来找到这个问题的决定,但我失败了。我做错了什么?我觉得我做错了什么,但我不明白到底是什么。
你为什么要这样做?
BaseConnection connection = (BaseConnection) DriverManager.getConnection(
"jdbc:postgresql://localhost:5432/m4",
"postgres",
"postgres"
);
此时您应该已经建立连接。由于您永远不会关闭此连接,因此您可能会在此处发生资源泄漏。
你大概可以做到
BaseConnection connection = (BaseConnection) ctx.statement().getConnection()
你应该试试 Connection.createArrayOf()
例如ctx.statement().getConnection().createArrayOf(...)
代码解决方案:
public class BigintArrayBinder implements Binding<Object[], Long[]> {
// The converter does all the work
@Override
public Converter<Object[], Long[]> converter() {
return new BigintArrayConverter();
}
// Rending a bind variable for the binding context's value and casting it to the bigint array type
@Override
public void sql(BindingSQLContext<Long[]> ctx) throws SQLException {
ctx.render().visit(DSL.inline(ctx.convert(converter()).value()));
}
// Registering ARRAY types for JDBC CallableStatement OUT parameters
@Override
public void register(BindingRegisterContext<Long[]> ctx) throws SQLException {
ctx.statement().registerOutParameter(ctx.index(), Types.ARRAY);
}
// Converting the Long[] to a bigint[] value and setting that on a JDBC PreparedStatement
@Override
public void set(BindingSetStatementContext<Long[]> ctx) {
Object[] value = ctx.convert(converter()).value();
ctx.dsl().connection(connection -> connection.createArrayOf("bigint", value));
}
// Getting a bigint[] value from a JDBC ResultSet and converting that to a Long[]
@Override
public void get(BindingGetResultSetContext<Long[]> ctx) throws SQLException {
Object[] array = (Object[]) ctx.resultSet().getArray(ctx.index()).getArray();
ctx.convert(converter()).value(array);
}
// Getting a bigint[] value from a JDBC CallableStatement and converting that to a Long[]
@Override
public void get(BindingGetStatementContext<Long[]> ctx) throws SQLException {
Object[] array = (Object[]) ctx.statement().getArray(ctx.index()).getArray();
ctx.convert(converter()).value(array);
}
// Setting a value on a JDBC SQLOutput (useful for Oracle OBJECT types)
@Override
public void set(BindingSetSQLOutputContext<Long[]> ctx) throws SQLException {
throw new SQLFeatureNotSupportedException();
}
// Getting a value from a JDBC SQLInput (useful for Oracle OBJECT types)
@Override
public void get(BindingGetSQLInputContext<Long[]> ctx) throws SQLException {
throw new SQLFeatureNotSupportedException();
}
}
所有感兴趣的人早上好 :) 我对 jOOQ 有一些疑问。
我需要从 PostgreSQL table 生成 jOOQ 实体。它有一个 bigint[]
类型的字段。 jOOQ Gradle 插件(我正在使用这个 https://github.com/etiennestuder/gradle-jooq-plugin)不支持数组 DateType,所以唯一的方法是使用带有强制类型的自定义类型
// jooq config in build.gradle
customTypes {
customType {
name = "BigintArrayBinder"
type = "Long[]"
converter = "ru.stdev.tskad.yandexnavigator.binder.BigintArrayBinder"
}
}
forcedTypes {
forcedType {
name = "BigintArrayBinder"
includeExpression = '.*'
includeTypes = 'ARRAY'
}
}
这是这个活页夹及其转换器的实现
public class BigintArrayConverter implements Converter<Object[], Long[]> {
@Override
public Long[] from(Object[] t) {
return t == null ? new Long[]{} : (Long[]) t;
}
@Override
public Object[] to(Long[] u) {
return u == null || u.length == 0 ? new Object[]{} : u;
}
@Override
public Class<Object[]> fromType() {
return Object[].class;
}
@Override
public Class<Long[]> toType() {
return Long[].class;
}
}
import org.jooq.*;
import org.jooq.conf.ParamType;
import org.jooq.impl.DSL;
import org.postgresql.core.BaseConnection;
import org.postgresql.jdbc.PgArray;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class BigintArrayBinder implements Binding<Object[], Long[]> {
// The converter does all the work
@Override
public Converter<Object[], Long[]> converter() {
return new BigintArrayConverter();
}
// Rending a bind variable for the binding context's value and casting it to the json type
@Override
public void sql(BindingSQLContext<Long[]> ctx) throws SQLException {
// Depending on how you generate your SQL, you may need to explicitly distinguish
// between jOOQ generating bind variables or inlined literals.
if (ctx.render().paramType() == ParamType.INLINED)
ctx.render().visit(DSL.inline(ctx.convert(converter()).value())).sql("::bigint[]");
else
ctx.render().sql("::bigint[]");
}
// Registering ARRAY types for JDBC CallableStatement OUT parameters
@Override
public void register(BindingRegisterContext<Long[]> ctx) throws SQLException {
ctx.statement().registerOutParameter(ctx.index(), Types.ARRAY);
}
// Converting the Long[] to a bigint[] value and setting that on a JDBC PreparedStatement
@Override
public void set(BindingSetStatementContext<Long[]> ctx) throws SQLException {
Object[] value = ctx.convert(converter()).value();
BaseConnection connection = (BaseConnection) DriverManager.getConnection(
"jdbc:postgresql://localhost:5432/m4",
"postgres",
"postgres"
);
int longOid = 20;
System.out.print("value -> ");
System.out.println(Arrays.toString(value));
PgArray pgArray = new PgArray(connection, longOid, Arrays.toString(value));
ctx.statement().setArray(ctx.index(), pgArray);
}
// Getting a bigint[] value from a JDBC ResultSet and converting that to a Long[]
@Override
public void get(BindingGetResultSetContext<Long[]> ctx) throws SQLException {
Object[] array = (Object[]) ctx.resultSet().getArray(ctx.index()).getArray();
ctx.convert(converter()).value(array);
}
// Getting a bigint[] value from a JDBC CallableStatement and converting that to a Long[]
@Override
public void get(BindingGetStatementContext<Long[]> ctx) throws SQLException {
Object[] array = (Object[]) ctx.statement().getArray(ctx.index()).getArray();
ctx.convert(converter()).value(array);
}
// Setting a value on a JDBC SQLOutput (useful for Oracle OBJECT types)
@Override
public void set(BindingSetSQLOutputContext<Long[]> ctx) throws SQLException {
throw new SQLFeatureNotSupportedException();
}
// Getting a value from a JDBC SQLInput (useful for Oracle OBJECT types)
@Override
public void get(BindingGetSQLInputContext<Long[]> ctx) throws SQLException {
throw new SQLFeatureNotSupportedException();
}
}
因此,它正在编译并且可以很好地用于选择查询。但是如果我尝试插入我有这个例外
org.jooq.exception.DataAccessException: SQL [null]; Error while writing value at JDBC bind index: 7
at org.jooq_3.13.1.POSTGRES.debug(Unknown Source)
at org.jooq.impl.Tools.translate(Tools.java:2751)
at org.jooq.impl.AbstractBindContext.bindValue(AbstractBindContext.java:127)
at org.jooq.impl.Val.accept(Val.java:103)
at org.jooq.impl.AbstractBindContext.bindInternal(AbstractBindContext.java:269)
at org.jooq.impl.AbstractBindContext.visit0(AbstractBindContext.java:88)
at org.jooq.impl.AbstractContext.visit0(AbstractContext.java:457)
at org.jooq.impl.AbstractContext.visit(AbstractContext.java:218)
at org.jooq.impl.QueryPartList.accept(QueryPartList.java:121)
at org.jooq.impl.AbstractBindContext.bindInternal(AbstractBindContext.java:269)
at org.jooq.impl.AbstractBindContext.visit0(AbstractBindContext.java:88)
at org.jooq.impl.AbstractContext.visit0(AbstractContext.java:457)
at org.jooq.impl.AbstractContext.visit(AbstractContext.java:218)
at org.jooq.impl.AbstractQuery.execute(AbstractQuery.java:367)
at org.jooq.impl.AbstractDelegatingQuery.execute(AbstractDelegatingQuery.java:119)
at ru.stdev.tskad.yandexnavigator.repository.NavigatorRepository$testInsert.invokeSuspend(NavigatorRepository.kt:37)
at ru.stdev.tskad.yandexnavigator.repository.NavigatorRepository$testInsert.invoke(NavigatorRepository.kt)
at ru.stdev.utp.common.jooq.DefaultJooqWrapper$launchOperation.invokeSuspend(DefaultJooqWrapper.kt:17)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:330)
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:26)
at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:109)
at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:158)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:58)
at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
at ru.stdev.utp.common.jooq.DefaultJooqWrapper.launchOperation(DefaultJooqWrapper.kt:16)
at ru.stdev.utp.common.jooq.DefaultJooqWrapper.request(DefaultJooqWrapper.kt:27)
at ru.stdev.tskad.yandexnavigator.repository.NavigatorRepository.testInsert(NavigatorRepository.kt:25)
at ru.stdev.tskad.yandexnavigator.routes.NavigatorRouteKt$navigator.invokeSuspend(NavigatorRoute.kt:23)
at ru.stdev.tskad.yandexnavigator.routes.NavigatorRouteKt$navigator.invoke(NavigatorRoute.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:318)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:163)
at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:183)
at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:27)
at io.ktor.routing.Routing.executeResult(Routing.kt:147)
at io.ktor.routing.Routing.interceptor(Routing.kt:34)
at io.ktor.routing.Routing$Feature$install.invokeSuspend(Routing.kt:99)
at io.ktor.routing.Routing$Feature$install.invoke(Routing.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:318)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:163)
at io.ktor.features.ContentNegotiation$Feature$install.invokeSuspend(ContentNegotiation.kt:107)
at io.ktor.features.ContentNegotiation$Feature$install.invoke(ContentNegotiation.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:318)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:163)
at io.ktor.features.StatusPages$interceptCall.invokeSuspend(StatusPages.kt:101)
at io.ktor.features.StatusPages$interceptCall.invoke(StatusPages.kt)
at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:91)
at kotlinx.coroutines.CoroutineScopeKt.coroutineScope(CoroutineScope.kt:194)
at io.ktor.features.StatusPages.interceptCall(StatusPages.kt:100)
at io.ktor.features.StatusPages$Feature$install.invokeSuspend(StatusPages.kt:140)
at io.ktor.features.StatusPages$Feature$install.invoke(StatusPages.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:318)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:163)
at io.ktor.features.CallLogging$Feature$install.invokeSuspend(CallLogging.kt:139)
at io.ktor.features.CallLogging$Feature$install.invoke(CallLogging.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:318)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:163)
at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:183)
at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:27)
at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline.invokeSuspend(DefaultEnginePipeline.kt:120)
at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline.invoke(DefaultEnginePipeline.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:318)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:163)
at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:183)
at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:27)
at io.ktor.server.netty.NettyApplicationCallHandler$handleRequest.invokeSuspend(NettyApplicationCallHandler.kt:40)
at io.ktor.server.netty.NettyApplicationCallHandler$handleRequest.invoke(NettyApplicationCallHandler.kt)
at kotlinx.coroutines.intrinsics.UndispatchedKt.startCoroutineUndispatched(Undispatched.kt:55)
at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:111)
at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:158)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:56)
at kotlinx.coroutines.BuildersKt.launch(Unknown Source)
at io.ktor.server.netty.NettyApplicationCallHandler.handleRequest(NettyApplicationCallHandler.kt:30)
at io.ktor.server.netty.NettyApplicationCallHandler.channelRead(NettyApplicationCallHandler.kt:24)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377)
at io.netty.channel.AbstractChannelHandlerContext.access0(AbstractChannelHandlerContext.java:59)
at io.netty.channel.AbstractChannelHandlerContext.run(AbstractChannelHandlerContext.java:368)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:164)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:472)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:500)
at io.netty.util.concurrent.SingleThreadEventExecutor.run(SingleThreadEventExecutor.java:989)
at io.netty.util.internal.ThreadExecutorMap.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:832)
Caused by: java.sql.SQLException: Error while writing value at JDBC bind index: 7
at org.jooq.impl.DefaultBindContext.bindValue0(DefaultBindContext.java:67)
at org.jooq.impl.AbstractBindContext.bindValue(AbstractBindContext.java:124)
... 81 common frames omitted
Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 63 out of bounds for length 63
at org.postgresql.jdbc.PgArray.buildArrayList(PgArray.java:450)
at org.postgresql.jdbc.PgArray.getBaseTypeName(PgArray.java:811)
at org.postgresql.jdbc.PgPreparedStatement.setArray(PgPreparedStatement.java:1098)
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.setArray(HikariProxyPreparedStatement.java)
at org.jooq.tools.jdbc.DefaultPreparedStatement.setArray(DefaultPreparedStatement.java:239)
at ru.stdev.tskad.yandexnavigator.binder.BigintArrayBinder.set(BigintArrayBinder.java:63)
at org.jooq.impl.DefaultBindContext.bindValue0(DefaultBindContext.java:62)
... 82 common frames omitted
首先让我感到困惑的是有必要创建 BaseConnection
。我可以从 DslContext 获得一个连接,但它 returns 一个 HikariProxyConnection
。因此,它不适用于 PgArray
,它需要 BaseConnection
。然后,我需要给它一个 oid
,等于 20 到 Long 类型。然后我必须给出一个数组作为字符串或字节数组。结果我有 ArrayIndexOutOfBoundsException
。我做了很多尝试来找到这个问题的决定,但我失败了。我做错了什么?我觉得我做错了什么,但我不明白到底是什么。
你为什么要这样做?
BaseConnection connection = (BaseConnection) DriverManager.getConnection(
"jdbc:postgresql://localhost:5432/m4",
"postgres",
"postgres"
);
此时您应该已经建立连接。由于您永远不会关闭此连接,因此您可能会在此处发生资源泄漏。
你大概可以做到
BaseConnection connection = (BaseConnection) ctx.statement().getConnection()
你应该试试 Connection.createArrayOf()
例如ctx.statement().getConnection().createArrayOf(...)
代码解决方案:
public class BigintArrayBinder implements Binding<Object[], Long[]> {
// The converter does all the work
@Override
public Converter<Object[], Long[]> converter() {
return new BigintArrayConverter();
}
// Rending a bind variable for the binding context's value and casting it to the bigint array type
@Override
public void sql(BindingSQLContext<Long[]> ctx) throws SQLException {
ctx.render().visit(DSL.inline(ctx.convert(converter()).value()));
}
// Registering ARRAY types for JDBC CallableStatement OUT parameters
@Override
public void register(BindingRegisterContext<Long[]> ctx) throws SQLException {
ctx.statement().registerOutParameter(ctx.index(), Types.ARRAY);
}
// Converting the Long[] to a bigint[] value and setting that on a JDBC PreparedStatement
@Override
public void set(BindingSetStatementContext<Long[]> ctx) {
Object[] value = ctx.convert(converter()).value();
ctx.dsl().connection(connection -> connection.createArrayOf("bigint", value));
}
// Getting a bigint[] value from a JDBC ResultSet and converting that to a Long[]
@Override
public void get(BindingGetResultSetContext<Long[]> ctx) throws SQLException {
Object[] array = (Object[]) ctx.resultSet().getArray(ctx.index()).getArray();
ctx.convert(converter()).value(array);
}
// Getting a bigint[] value from a JDBC CallableStatement and converting that to a Long[]
@Override
public void get(BindingGetStatementContext<Long[]> ctx) throws SQLException {
Object[] array = (Object[]) ctx.statement().getArray(ctx.index()).getArray();
ctx.convert(converter()).value(array);
}
// Setting a value on a JDBC SQLOutput (useful for Oracle OBJECT types)
@Override
public void set(BindingSetSQLOutputContext<Long[]> ctx) throws SQLException {
throw new SQLFeatureNotSupportedException();
}
// Getting a value from a JDBC SQLInput (useful for Oracle OBJECT types)
@Override
public void get(BindingGetSQLInputContext<Long[]> ctx) throws SQLException {
throw new SQLFeatureNotSupportedException();
}
}