【问题标题】:How to map sql DATE to LocalDate如何将sql DATE映射到LocalDate
【发布时间】:2016-10-13 06:17:20
【问题描述】:

我想将LocalDate 存储在DATE 列中并按原样检索它。根据定义,DATELocalDate 都是“本地”类型。因此,时区的概念不应以任何方式干扰。

下面的代码是一个在内存数据库中创建具有DATE 列的表的最小示例。 maven 工件com.h2database:h2:1.4.192 必须在类路径中。

首先,定义方法insertretrieve

static void insert(DataSource ds, String date) throws SQLException {
  try (Connection conn = ds.getConnection();
       Statement stmt = conn.createStatement()) {
    stmt.execute("CREATE TABLE people (id BIGINT NOT NULL AUTO_INCREMENT"
      + ", born DATE NOT NULL, PRIMARY KEY (id) );");
    stmt.execute("INSERT INTO people (born) VALUES ('" + date + "')");
  }
}

static LocalDate retrieve(DataSource ds) throws SQLException {
  try (Connection conn = ds.getConnection();
       Statement stmt = conn.createStatement();
       ResultSet rs = stmt.executeQuery("SELECT * FROM people limit 1")) {
    if (rs.next()) {
      java.sql.Date retrieved = java.sql.Date.valueOf(rs.getString("born"));
      return retrieved.toLocalDate();
    }
    throw new IllegalStateException("No data");
  }
}

注意insert 方法使用单引号中的LocalDatetoString 值,因此Java™ 没有机会创建时区歧义。现在调用insert 一次然后多次retrieve,每次使用不同的时区设置:

public static void main(String[] args) throws Exception {
  DataSource ds = JdbcConnectionPool.create("jdbc:h2:mem:test", "sa", "sa");
  LocalDate born = LocalDate.parse("2015-05-20");
  insert(ds, born.toString());
  System.out.println("Inserted:  " + born);
  for (int i : new int[]{-14, 0, 12}) {
    TimeZone z = TimeZone.getTimeZone(String.format("Etc/GMT%+02d", i));
    TimeZone.setDefault(z);
    System.out.println("Retrieved: " + retrieve(ds));
  }
}

然后打印以下内容:

插入: 2015-05-20 检索时间:2015-05-20 检索时间:2015-05-19 检索时间:2015-05-18

假设数据库表没有改变,如何编写retrieve方法,使其返回插入无条件的相同值?

【问题讨论】:

  • 你在导入 java.util.Date 吗?在 retrieved = 行中,您在分配的左侧使用完全限定的 java.sql.Date 而不是右侧。
  • 您为什么希望打印 3 个相同的 LocalDate?您正在插入日期2015-05-20。然后,当您更改默认时区时,java.sql.Date.toLocalDate 方法将返回不同的结果。
  • @BasilBourque 已修复
  • 为了不被隐含地绑定到系统时区,我希望(或希望)在这里出错,但我只会使用Stringreturn LocalDate.parse(rs.getString("born"));...
  • @LarsBohl 我认为它只有效,因为 H2 存储 DATE as yyyy-MM-dd,而不是我推荐的东西。但是,我发现这篇文章stackoverflow.com/questions/32548331/… 并且问题中的代码似乎可以解决这个问题(即,如果您使用提供的getLocalDate)。

标签: java jdbc timezone java-8 sqldatetime


【解决方案1】:

我刚刚尝试对您的 retrieve 方法进行以下修改,它对我有用:

H2 documentation for the DATE Type 说是

日期数据类型。格式为 yyyy-MM-dd。

所以,而不是你的...

java.sql.Date retrieved = (java.sql.Date) rs.getObject("born");
return retrieved.toLocalDate();

...我刚用过...

return LocalDate.parse(rs.getString("born"));

...和我的代码生成

Inserted:  2015-05-20
Retrieved: 2015-05-20
Retrieved: 2015-05-20
Retrieved: 2015-05-20

【讨论】:

  • 对我来说看起来很安全,除了可能是 null 处理。谢谢!我仍然想知道如何使用 Oracle DB 而不是 H2(它在 DATE 中存储一秒的精度)来做到这一点。不过,那将是一个不同的问题。
【解决方案2】:

以下解决方案也有效。我更喜欢在接受的答案中通过String 进行转换,因为它避免了下面显示的时区修补。然而,它可能不会在所有数据库上以相同的方式工作,因为有些,例如Oracle,DATE 的定义不同。

static LocalDate retrieve(DataSource ds) throws SQLException {
  try (Connection conn = ds.getConnection();
       Statement stmt = conn.createStatement();
       ResultSet rs = stmt.executeQuery("SELECT * FROM people limit 1")) {
    if (rs.next()) {
      ZoneId utc = ZoneId.of("UTC");
      TimeZone z = TimeZone.getTimeZone(utc);
      Calendar cal = Calendar.getInstance(z);
      java.sql.Date retrieved = rs.getDate("born", cal);
      long time = retrieved.getTime();
      java.util.Date utilDate = new java.util.Date(time);
      Instant instant = utilDate.toInstant();
      ZonedDateTime zdt = instant.atZone(utc);
      return zdt.toLocalDate();
    }
  }
  throw new IllegalStateException("No data");
}

根据用户 Tunaki 的建议,此问题中概述了通过 java.util.Date 进行的转换:Missed opportunity to fix JDBC date handling in Java 8?

【讨论】:

    猜你喜欢
    • 2015-05-23
    • 2019-04-02
    • 1970-01-01
    • 1970-01-01
    • 2023-03-25
    • 2021-02-06
    • 1970-01-01
    • 2021-12-07
    • 2017-11-04
    相关资源
    最近更新 更多