合并 OffsetDateTime 时,当偏移为 UTC (+00:00) 时失败。

huangapple go评论87阅读模式
英文:

Merge fails for OffsetDateTime when offset is UTC (+00:00)

问题

出现问题的代码逻辑是关于 HSQLDB 和 MERGE 语句中 OffsetDateTime 的使用,特别是在 UTC 偏移(+00:00)时出现的问题。解决方案是通过自定义 DateTimeFormatter 格式化 OffsetDateTime,将其偏移表示为 "+00:00" 形式。这样可以解决 MERGE 语句中的问题。

private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder()
        .appendPattern("yyyy-MM-dd HH:mm:ss.SSS")
        .appendOffset("+HH:MM", "+00:00")
        .toFormatter();

private int merge(String code, OffsetDateTime updated) throws SQLException {
    try (PreparedStatement statement = connection.prepareStatement(MERGE_SQL)) {
        statement.setString(1, code);
        statement.setString(2, FORMATTER.format(updated));
        return statement.executeUpdate();
    }
}
英文:

I’m having an issue with HSQLDB and MERGE with the following merge logic if I use an OffsetDateTime with offset +00:00

    private static final String CREATE_TABLE_SQL = 
            "create table sample (" + 
               "code varchar2(50), " + 
               "updated timestamp with time zone" + 
            ")";

    private static final String MERGE_SQL =
            "merge into sample t " +
            "using (select ? as code, ? as updated from dual) val " +
            "on (t.code = val.code) " +
            "when matched then update set t.updated = val.updated " +
            "when not matched then insert(code, updated) values (val.code, val.updated)";

    private int merge(String code, OffsetDateTime updated) throws SQLException {
        try (PreparedStatement statement = connection.prepareStatement(MERGE_SQL)) {
            statement.setString(1, code);
            statement.setObject(2, updated, Types.TIMESTAMP_WITH_TIMEZONE);
            return statement.executeUpdate();
        }
    }

The merge statement works fine for all offsets except UTC (eg offset +01:00 works)
I think this is caused by the fact that toString() of an OffsetDateTime at UTC has the Z suffix instead of +00:00. It seems that an INSERT statement does not have the same problem as MERGE

Exception stack trace

java.sql.SQLDataException: data exception: invalid datetime format
            at org.hsqldb.jdbc.JDBCUtil.sqlException(Unknown Source)
            at org.hsqldb.jdbc.JDBCUtil.sqlException(Unknown Source)
            at org.hsqldb.jdbc.JDBCPreparedStatement.fetchResult(Unknown Source)
            at org.hsqldb.jdbc.JDBCPreparedStatement.executeUpdate(Unknown Source)
            at com.sample.DeletemeHsqlBugTest.merge(DeletemeHsqlBugTest.java:79)
            at com.sample.DeletemeHsqlBugTest.testMerge(DeletemeHsqlBugTest.java:58)
            at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
            ...
Caused by: org.hsqldb.HsqlException: data exception: invalid datetime format
            at org.hsqldb.error.Error.error(Unknown Source)
            at org.hsqldb.error.Error.error(Unknown Source)
            at org.hsqldb.types.DateTimeType.convertToDatetimeSpecial(Unknown Source)
            at org.hsqldb.types.DateTimeType.convertToType(Unknown Source)
            at org.hsqldb.ExpressionOp.getValue(Unknown Source)
            at org.hsqldb.StatementDML.getInsertData(Unknown Source)
            at org.hsqldb.StatementDML.executeMergeStatement(Unknown Source)
            at org.hsqldb.StatementDML.getResult(Unknown Source)
            at org.hsqldb.StatementDMQL.execute(Unknown Source)
            at org.hsqldb.Session.executeCompiledStatement(Unknown Source)
            at org.hsqldb.Session.execute(Unknown Source)
            ... 73 more

As a workaround I can do the following which feels hacky

    private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder()
            .appendPattern("yyyy-MM-dd HH:mm:ss.SSS")
            .appendOffset("+HH:MM", "+00:00")
            .toFormatter();

    private int merge(String code, OffsetDateTime updated) throws SQLException {
        try (PreparedStatement statement = connection.prepareStatement(MERGE_SQL)) {
            statement.setString(1, code);
            statement.setString(2, FORMATTER.format(updated));
            return statement.executeUpdate();
        }
    }

答案1

得分: 2

我在hsqldb-user邮件列表上发布了这个问题,并收到了以下回复,解决了我的问题。我在这里重新发布,因为在Stack Overflow上更容易找到:

> 如果您将所需类型添加到CAST,它应该可以工作:

"using (select ? as code, cast(? as timestamp with time zone) as updated from dual) val " +

更新

似乎这个问题已经修复,并将在HSQLDB的下一个版本中提供:

英文:

I posted this question on the hsqldb-user mailing list and got the following response which fixes my issue. Re-posting here since it's easier to find it here on Stack Overflow

> it should work if you add a CAST to the required type:

"using (select ? as code, cast(? as timestamp with time zone) as updated from dual) val " +

Update

It seems that this issue has been fixed and will be available in the next release of HSQLDB

huangapple
  • 本文由 发表于 2023年6月12日 16:58:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/76455033.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定