我有一个非常简单的JOOG查询:

jooq.select(TABLE_NAME.fields())
                .from(TABLE_NAME)
                .where(TABLE_NAME.ID.in(ids))
                .fetchInto(tableDTO.class);

其中id为List<BigInteger>

JOOG生成的查询:

select 
"schema"."table_name"."id",
"schema"."table_name"."entity_id", 
"schema"."table_name"."code", 
"schema"."table_name"."created_date", 
...
from "schema"."table_name" where "schema"."table_name"."id" in (3623)

表格DTO:

@Data
public class tableDTO{
    private BigInteger id;
...

表DDL:

create table table_name
(
    id bigint default nextval('schema.table_name_id_seq'::regclass) not null primary key,
...

此外,该表还有一个forcedType,用于将字段ID转换为BigHandler:

<forcedType>
 <name>DECIMAL_INTEGER</name>
 <includeExpression>.*\.(TABLE_NAME)\.(ID|PARENT_ID|ENTITY_ID)</includeExpression>
</forcedType>

Issue

当我通过JOOQ运行这个查询时,它会以某种方式将bigint的列表转换为numeric,并且postquist使用并行seq扫描而不是索引扫描.

通过打印jooq查询计划我可以看到:

Fetched result: +----------------------------------------------------------------------------------+
|QUERY PLAN                                                                        |
+----------------------------------------------------------------------------------+
|Gather  (cost=1000.00..1561941.21 rows=65315 width=1896)                          |
|  Workers Planned: 2                                                              |
|  ->  Parallel Seq Scan on table_name (cost=0.00..1554409.71 rows=27215 width=1896)|
|        Filter: ((id)::numeric = '3623'::numeric)                                 |
+----------------------------------------------------------------------------------+

但当我通过DataGrip运行完全相同的查询时,Postgs正在使用索引扫描:

Index Scan using table_name_pkey on table_name (cost=0.43..2.65 rows=1 width=1896)
  Index Cond: (id = 3623)

我已经try 过吸尘、分析这张桌子了.看起来我的桌子非常好,而且不知何故jooq产生了这些错误的演员

问题

为什么会发生这种隐性演员阵容?这是JOOG的错还是DB的错?

推荐答案

为什么会发生这种

您应用了data type rewrite,这意味着jOOQ的代码生成删除了原始数据类型(BIGINT)的所有知识,并假装PostgreSQL INFORMATION_SCHEMA.COLUMNS视图报告的数据类型确实是NUMERIC.

现在,出于多种原因,jOOQ倾向于将PostgreSQL中的绑定值强制转换为例如CAST(? AS NUMERIC),因为PostgreSQL往往无法推断类型,因此当PostgreSQL看到这样的比较时:

BIGINT_COLUMN = CAST(? AS NUMERIC)

那么在PostgreSQL中如何处理这个问题有4个 Select :

  • 删除"不兼容"的比较(不受欢迎)
  • 为所有可能的类型对指定=操作员(不切实际)
  • 推广不太精确的类型(BIGINTNUMERIC).这是首选的解决方案,因为它没有损耗
  • "降级"更精确的类型(NUMERICBIGINT)(不可取,因为有损)

但当BIGINT_COLUMN提升到NUMERIC时,优化器似乎无法使用索引,除非该索引是基于CAST(BIGINT_COLUMN AS NUMERIC)的索引的函数.jOOQ博客记录了类似的不受欢迎Oracle DATE -> TIMESTAMP promotions问题.

如何修复它

当数据库只能存储Long个值时,我不知道为什么您的客户端代码中需要BigInteger类型.最简单的解决方案是不这样做,而是使用Long.如果您更喜欢BigInteger,您可以:

  • 更改模式以存储NUMERIC个值(这将防止存储太大ID时溢出)
  • Converter<Long, BigInteger>附加到列,而不是应用数据类型重写.这样,jOOQ将在客户端代码中生成BigInteger个值,而不会丢失有关基础列类型的信息.

Java相关问答推荐

Java字符串常数池困惑

Mongo DB Bson和Java:在子文档中添加和返回仅存在于父文档中的字段?

Java事件系统通用转换为有界通配符

无法找到符号错误—Java—封装

无法传递消费者<;>;实例

使用意向过滤器从另一个应用程序启动服务

不推荐使用的Environment.getExternalStorageDirectory().getAbsolutePath()返回的值不同于新的getExternalFilesDir(空)?

多重延迟签名

将Spring Boot 3.2.0升级到3.2.1后查询执行错误

从ActiveMQ Classic迁移到ActiveMQ Artemis需要进行哪些客户端更改?

Java17支持哪个MapR版本?

在处理2个映射表时,没有更多的数据可从套接字读取

如果List是一个抽象接口,那么Collectors.toList()如何处理流呢?

项目react 堆中doOnComplete()和Subscribe()的第三个参数之间的差异

为什么创建Java动态代理需要接口参数

如何通过用户ID向用户发送私信

如何在Spring Boot Auth服务器上正确配置CORS?

如何使用jOOQ在PostgreSQL中从枚举类型生成Java枚举

Cucumber java-maven-示例表-未定义一步

由于可为null,无法在kotlin中实现java接口