数值超出范围减去 jOOQ 中的日期

Numeric value out of range subtracting dates in jOOQ

我正在尝试使用 H2 数据库 (v1.4.185) 比较 jOOQ (v3.5.0) 中的日期,但如果比较太大,我会得到 "Numeric value out of range"。

例如,使用以下 table

CREATE TABLE example (
    id INT AUTO_INCREMENT,
    deadline TIMESTAMP,
    PRIMARY KEY (id)
);

并使用以下示例代码(未显示 jOOQ 的代码生成步骤)

DSLContext dsl = DSL.using(getDataSource(dbUrl), SQLDialect.H2);

ExampleRecord exampleRecord = dsl.newRecord(EXAMPLE);
exampleRecord.setDeadline(Timestamp.valueOf("2001-01-10 12:15:30"));
exampleRecord.store();

dsl.selectFrom(EXAMPLE)
        .where(EXAMPLE.DEADLINE.sub(
              DayToSecond.valueOf(Duration.ofDays(30).toMillis()))
        .le(currentTimestamp()))
        .fetch().forEach(record -> System.out.println(record.getDeadline()));

产生以下错误

Exception in thread "main" org.jooq.exception.DataAccessException: 
SQL [select "EXAMPLE"."ID", "EXAMPLE"."DEADLINE" from "EXAMPLE" 
where dateadd('ms', cast(? as bigint), "EXAMPLE"."DEADLINE") 
    <= current_timestamp()]; 
Numeric value out of range: "-2592000000"; 
SQL statement:
        select "EXAMPLE"."ID", "EXAMPLE"."DEADLINE" from "EXAMPLE" 
where dateadd('ms', cast(? as bigint), "EXAMPLE"."DEADLINE") 
    <= current_timestamp() [22003-185]

这个错误对我来说没有意义。生成的 SQL (bigint) 表明 jOOQ 知道参数是 long 并且 -2592000000 完全在限制范围内。

我想知道错误消息是否添加了引号,或者如果 DayToSecond.toMillis() 可能 returns 是 String 而不是 long。除了我会期待一个不同的错误消息(加上一个编译错误)。

为了安全起见,尝试用常量替换代码来降低表达式的复杂性:

dsl.selectFrom(EXAMPLE)
    .where(EXAMPLE.DEADLINE.sub(-2592000000L))
    ...

只是为了看看这对错误有何影响。

在我看来,这像是 H2 中的一个缺陷。 H2 的DATEADD() function expects the number of units added to be of type int. That doesn't make sense when operating with milliseconds, of course. I've reported it on the H2 user group。让我们看看他们怎么说。如果这不能在 H2 中得到纠正,我们将在 jOOQ 中解决它,就像我们对 Sybase 和其他数据库所做的那样。

解决方法:

Field.sub(Number) 方法在用于日期时间算术时已经从时间戳中减去天数(如 Oracle)。因此,您 可以 编写与以下相同的表达式:

EXAMPLE.DEADLINE.sub(30)

另一种选择是使用 DSL.timestampAdd() 代替:

timestampAdd(EXAMPLE.DEADLINE, -30, DatePart.DAY);

第三种选择是使用普通的 SQL:

public static Field<Timestamp> mySub(Field<Timestamp> field, Number days) {
    return DSL.field("dateadd('day', {0}, {1})", Timestamp.class, val(-days), field);
}