JOOQ 和 Firebird - 超出实施限制
JOOQ & Firebird - Implementation Limit Exceeded
背景:我正在使用 jOOQ 访问 Firebird 数据库。 Firebird 2.x 的行大小限制为 64KB。我以前从未达到过限制,但是这个特定的数据库使用 UTF8,这意味着限制缩小到大约 16K 个字符。
这就是我使用 jOOQ 的方式:
根据需要加载或创建 POJO(已生成)。例如:
Book book = context.fetchOne("select * from book where book_id = ?", 1).into(Book.class);
根据需要使用书籍对象。
如果用户保存更改,将其作为记录存回。
BookRecord rec = context.newRecord(Tables.BOOK, book);
context.executeUpdate(rec);
步骤 3 在 executeUpdate()
方法上失败,因为 jOOQ 以某种方式将所有空的 VARCHAR
字段转换为 VARCHAR(4000)
。错误信息是
"SQL error code = -204. Implementation limit exceeded. block size
exceeds implementation restriction".
这是一个已知的 Firebird 限制,不幸的是,我们对此无能为力。
在 table 我有大约 8 个空的 VARCHAR(512)
字段,在 UTF8 中应该是最大 8x4x512(或 16KB),但由于 jOOQ 将其解释为 VARCHAR(4000)
,那是 8x4x4000 (128KB),这显然超出了限制。
如果我将 NULL
或空字段设置为某个随机字符串,那么 jOOQ 会将其转换为准确的字符串长度。 ("ABC" 将转换为 varchar(3)
)
所以我的问题是:如何让 executeUpdate()
在没有 jOOQ 将我的空字段转换为 VARCHAR(4000)
的情况下工作?
这些转换在 jOOQ 中是历史性的,因为一些数据库很难单独从绑定变量推断数据类型,因为它们不能延迟任何决定,直到查询被执行:
SELECT ? -- What's the type? Unknown at parse time.
这就是 jOOQ 为这些数据库生成显式转换的原因,其中包括 Firebird:
SELECT cast(? as varchar(4000)) -- Now, the type is clear
目前甚至在语句/子句中也这样做,其中类型可以从上下文中推断出来,包括
INSERT INTO t(a, b) VALUES (?, ?)
-- ^ ^ | |
-- +--|----------+ |
-- +-------------+ Bind types can be inferred from column type
在 Firebird 的情况下,不幸的是这种做法 运行 很快就进入了上述大小限制。在 jOOQ 中有一些未决的功能请求来改进这一点,但尚未在 jOOQ 3.9 中实现:
- #1735 Add Setting to indicate that bind values should not be cast
- #3398 Avoid generating new SQL statements for every bind variable length in Firebird
解决方法
如果你想在你这边修补 jOOQ,演员决定在 DefaultBinding.sql(BindingSQLContext)
中做出。你会看到有几个选项如何覆盖当前行为:
- 使用
Setting.paramCastMode == NEVER
关闭投射(可从 jOOQ 3.10 使用 #1735)
- 重写
RenderContext.castMode()
以始终产生 NEVER
- 在您自己的自定义绑定中覆盖整个方法
- 修补逻辑以从不应用任何强制转换
- 实施
ExecuteListener
,在您的 SQL 字符串中用 ?
替换 cast\(\?[^)]*\)
背景:我正在使用 jOOQ 访问 Firebird 数据库。 Firebird 2.x 的行大小限制为 64KB。我以前从未达到过限制,但是这个特定的数据库使用 UTF8,这意味着限制缩小到大约 16K 个字符。
这就是我使用 jOOQ 的方式:
根据需要加载或创建 POJO(已生成)。例如:
Book book = context.fetchOne("select * from book where book_id = ?", 1).into(Book.class);
根据需要使用书籍对象。
如果用户保存更改,将其作为记录存回。
BookRecord rec = context.newRecord(Tables.BOOK, book); context.executeUpdate(rec);
步骤 3 在 executeUpdate()
方法上失败,因为 jOOQ 以某种方式将所有空的 VARCHAR
字段转换为 VARCHAR(4000)
。错误信息是
"SQL error code = -204. Implementation limit exceeded. block size exceeds implementation restriction".
这是一个已知的 Firebird 限制,不幸的是,我们对此无能为力。
在 table 我有大约 8 个空的 VARCHAR(512)
字段,在 UTF8 中应该是最大 8x4x512(或 16KB),但由于 jOOQ 将其解释为 VARCHAR(4000)
,那是 8x4x4000 (128KB),这显然超出了限制。
如果我将 NULL
或空字段设置为某个随机字符串,那么 jOOQ 会将其转换为准确的字符串长度。 ("ABC" 将转换为 varchar(3)
)
所以我的问题是:如何让 executeUpdate()
在没有 jOOQ 将我的空字段转换为 VARCHAR(4000)
的情况下工作?
这些转换在 jOOQ 中是历史性的,因为一些数据库很难单独从绑定变量推断数据类型,因为它们不能延迟任何决定,直到查询被执行:
SELECT ? -- What's the type? Unknown at parse time.
这就是 jOOQ 为这些数据库生成显式转换的原因,其中包括 Firebird:
SELECT cast(? as varchar(4000)) -- Now, the type is clear
目前甚至在语句/子句中也这样做,其中类型可以从上下文中推断出来,包括
INSERT INTO t(a, b) VALUES (?, ?)
-- ^ ^ | |
-- +--|----------+ |
-- +-------------+ Bind types can be inferred from column type
在 Firebird 的情况下,不幸的是这种做法 运行 很快就进入了上述大小限制。在 jOOQ 中有一些未决的功能请求来改进这一点,但尚未在 jOOQ 3.9 中实现:
- #1735 Add Setting to indicate that bind values should not be cast
- #3398 Avoid generating new SQL statements for every bind variable length in Firebird
解决方法
如果你想在你这边修补 jOOQ,演员决定在 DefaultBinding.sql(BindingSQLContext)
中做出。你会看到有几个选项如何覆盖当前行为:
- 使用
Setting.paramCastMode == NEVER
关闭投射(可从 jOOQ 3.10 使用 #1735) - 重写
RenderContext.castMode()
以始终产生 NEVER - 在您自己的自定义绑定中覆盖整个方法
- 修补逻辑以从不应用任何强制转换
- 实施
ExecuteListener
,在您的 SQL 字符串中用?
替换cast\(\?[^)]*\)