简体   繁体   English

如何将SQL DATE映射到LocalDate

[英]How to map sql DATE to LocalDate

I want to store a LocalDate in a DATE column and retrieve it unchanged. 我想将LocalDate存储在DATE列中并保持不变。 Both DATE and LocalDate are "local" types by definition. 根据定义, DATELocalDate均为“本地”类型。 Therefore, the concept of timezone should not interfere in any way. 因此, 时区的概念不应以任何方式干涉。

The code below is a minimal example that creates a table with a DATE column in a in-memory database. 下面的代码是一个最小示例,该示例在内存数据库中创建一个带有DATE列的表。 The maven artifact com.h2database:h2:1.4.192 must be in the classpath. Maven工件com.h2database:h2:1.4.192必须在类路径中。

First, define methods insert and retrieve : 首先,定义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");
  }
}

Notice that the insert method uses the toString value of the LocalDate in single quotes, so there's no opportunity for Java™ to create timezone ambiguity. 注意, insert方法在单引号中使用LocalDatetoString值,因此Java™没有机会创建时区歧义。 Now call insert once and then several times retrieve , with different timzone settings each time: 现在打电话insert一次,然后多次retrieve ,每次用不同的timzone设置:

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));
  }
}

Then the following is printed: 然后打印以下内容:

Inserted:  2015-05-20
Retrieved: 2015-05-20
Retrieved: 2015-05-19
Retrieved: 2015-05-18

How to write the retrieve method so that it returns the same value that was inserted unconditionally , assuming that the database table doesn't change? 假设数据库表不变,如何编写retrieve方法,使其返回无条件插入的相同值?

I just tried the following modification to your retrieve method and it worked for me: 我只是尝试对您的retrieve方法进行以下修改,但对我有用:

The H2 documentation for the DATE Type says that it is DATE类型H2文档说它是

The date data type. 日期数据类型。 The format is yyyy-MM-dd. 格式为yyyy-MM-dd。

So, instead of your ... 所以,而不是你的...

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

... I just used ... 我刚用过

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

... and my code produced ...和我的代码产生

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

The following solution also works. 以下解决方案也适用。 I prefer the conversion via String in the accepted answer, because it avoids the timezone tinkering shown below. 我更喜欢在接受的答案中通过String进行转换,因为它避免了如下所示的时区调整。 It may however not work the same way on all databases because some, eg Oracle, have a different definition of DATE . 但是,它可能无法在所有数据库上以相同的方式工作,因为某些数据库(例如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");
}

The conversion via java.util.Date is outlined in this question, as suggested by user Tunaki: Missed opportunity to fix JDBC date handling in Java 8? 正如用户Tunaki所建议的那样,此问题概述了通过java.util.Date的转换:是否缺少在Java 8中修复JDBC日期处理的机会?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM