【问题标题】:jooq Converter: from java.sql.Date to java.time.LocalDatejooq 转换器:从 java.sql.Date 到 java.time.LocalDate
【发布时间】:2014-10-15 10:50:29
【问题描述】:

我已尝试编写 Converter<java.sql.Date, java.time.LocalDate>,但无法使其适用于所有时区设置。

想法:

  • 如果客户端代码有一个LocalDate,比如 2014 年 8 月 20 日,并将其保存到数据库中,则无论客户端时区是什么,它都应在数据库中显示为 2014 年 8 月 20 日。
  • 如果数据库包含 2014 年 8 月 20 日的日期,无论客户端时区是什么,客户端都应收到 2014 年 8 月 20 日的 LocalDate。

我的测试:

@Test public void dateConverter() {
  for (int offset = -12; offset <= 12; offset++) {
    TimeZone localTz = TimeZone.getTimeZone(ZoneOffset.ofHours(offset));
    TimeZone.setDefault(localTz);
    LocalDate ld = LocalDate.now();

    sql.insertInto(DATE_TEST).set(new DateTestRecord(ld)).execute();
    LocalDate savedLd = sql.selectFrom(DATE_TEST).fetchOne(DATE_TEST.DATE_);
    assertEquals(savedLd, ld, "offset=" + offset);
    sql.delete(DATE_TEST).execute();
  }
}

我的转换器:

public class DateConverter implements Converter<Date, LocalDate>{
  @Override public LocalDate from(Date date) { return date.toLocalDate(); }
  @Override public Date to(LocalDate ld) { return Date.valueOf(ld); }
  @Override public Class<Date> fromType() { return Date.class; }
  @Override public Class<LocalDate> toType() { return LocalDate.class; }
}

我尝试了各种变体,但都没有奏效......

【问题讨论】:

    标签: java sql jdbc jooq java-time


    【解决方案1】:

    问题其实出在测试中! JDBC 驱动程序在创建时区时对其进行缓存,并且不考虑测试循环中的时区更新。每次测试中的时区更改时都建立一个新的连接使其通过。

    因此,问题中的代码适用于 Date 到 LocalDate 转换器(除了它应该接受 null)。最终版本:

    public class DateConverter implements Converter<Date, LocalDate> {
      @Override public LocalDate from(Date date) { return date == null ? null : date.toLocalDate(); }
      @Override public Date to(LocalDate ld) { return ld == null ? null : Date.valueOf(ld); }
      @Override public Class<Date> fromType() { return Date.class; }
      @Override public Class<LocalDate> toType() { return LocalDate.class; }
    }
    

    时区时间到 OffsetTime 转换器可以用类似的方式完成:

    public class TimeConverter implements Converter<Time, OffsetTime> {
      @Override public OffsetTime from(Time time) {
        return time == null ? null : OffsetTime.ofInstant(Instant.ofEpochMilli(time.getTime()), ZoneOffset.systemDefault());
      }
      @Override public Time to(OffsetTime offsetTime) {
        return offsetTime == null ? null : new Time(offsetTime.atDate(LocalDate.ofEpochDay(0)).toInstant().toEpochMilli());
      }
      @Override public Class<Time> fromType() { return Time.class; }
      @Override public Class<OffsetTime> toType() { return OffsetTime.class; }
    }
    

    【讨论】:

    • “JDBC 驱动缓存时区”:嗯,什么 JDBC 驱动能做到这一点?请注意,jOOQ User Group 上正在进行关于 TIMESTAMP WITH TIMEZONE 数据类型的讨论。
    • 我无法获得具有 timestamptz 工作的类似转换器 - 也许相关? (是postgres最新驱动)
    • 是的,这绝对是相关的。 The relevant issue is probably this one here
    • 你确定JDBC驱动真的缓存了时区吗?我期望的是驱动程序在建立连接并为连接设置时使用当前默认时区。此时区设置保持有效,直到连接客户端使用 SET TIME ZONE 等更改时区。在这种情况下,仅更改 JRE 的默认时区不会影响已建立的与数据库的连接和会话,但时区根本不会被缓存。
    • 只是在建立会话期间,驱动程序会传达当前时区,然后由连接客户端根据需要更改时区。否则,驱动程序只会以不可预知的方式使用您的会话传达“事物”并发送命令。我想如果您也使用数据库客户端的命令更改会话时区,您的测试会成功。
    猜你喜欢
    • 1970-01-01
    • 2021-05-27
    • 2019-04-09
    • 2017-09-14
    • 1970-01-01
    • 2017-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多