jooq转换器:从java.sql.Date到java.time.LocalDate

6
我尝试编写了一个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; }
}

我尝试了各种变化,但都无法解决...

1个回答

2
问题实际上出现在测试中! 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用户组上正在讨论“带有时区的TIMESTAMP”数据类型。 - Lukas Eder
1
是的,那肯定有关联。相关问题可能在这里:https://github.com/jOOQ/jOOQ/issues/2738 - Lukas Eder
你确定JDBC驱动程序真的缓存时区吗?我期望的是,当驱动程序建立连接并设置连接时,驱动程序使用当前默认时区。该时区设置保持有效,直到连接客户端使用SET TIME ZONE等命令更改时区。在这种情况下,只更改JRE的默认时区不会影响已建立的连接和与数据库的会话,而且时区根本没有被缓存。 - Thorsten Schöning
@LukasEder FYI,timestamptz转换器的问题是因为我没有在“timestamp\ with\ time\ zone”中转义空格:https://dev59.com/_ZDea4cB1Zd3GeqPXyrQ。这是有意为之吗?(在普通Java中,“timezone with time zone”.matches("timezone with time zone")无论空格是否被转义都是true) - assylias
@assylias:我注意到了另一个问题,我会在那里发表评论。这不好 - 当我们打开Java正则表达式的COMMENTS标志时,我们忽视了这个影响... - Lukas Eder
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,