7

我想将 a 存储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方法使用单引号中的toStringLocalDate,因此 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方法以使其返回无条件插入的相同值?

4

2 回答 2

6

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

DATE 类型的H2 文档说它是

日期数据类型。格式为 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
于 2016-06-13T15:43:32.127 回答
1

以下解决方案也有效。我更喜欢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");
}

java.util.Date正如用户 Tunaki 所建议的,此问题中概述了转换方式:错过了在 Java 8 中修复 JDBC 日期处理的机会?

于 2016-06-13T17:16:49.870 回答