如何将 sql DATE 映射到 LocalDate
How to map sql DATE to LocalDate
我想将 LocalDate
存储在 DATE
列中并在原样检索它。根据定义,DATE
和 LocalDate
都是 "local" 类型。因此,timezone 的概念不应以任何方式干扰。
下面的代码是一个最小的示例,它在内存数据库中创建一个带有 DATE
列的 table。 Maven 工件 com.h2database:h2:1.4.192
必须在类路径中。
首先定义方法insert
和retrieve
:
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
方法在单引号中使用 LocalDate
的 toString
值,因此 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));
}
}
然后打印如下:
Inserted: 2015-05-20
Retrieved: 2015-05-20
Retrieved: 2015-05-19
Retrieved: 2015-05-18
如何编写 retrieve
方法,使其 returns 与 无条件 插入的值相同,假设数据库 table没有改变?
我刚刚尝试对您的 retrieve
方法进行以下修改,它对我有用:
H2 documentation for the DATE Type说的是
The date data type. The format is 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
以下解决方案也有效。我更喜欢在接受的答案中通过 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
进行的转换:
我想将 LocalDate
存储在 DATE
列中并在原样检索它。根据定义,DATE
和 LocalDate
都是 "local" 类型。因此,timezone 的概念不应以任何方式干扰。
下面的代码是一个最小的示例,它在内存数据库中创建一个带有 DATE
列的 table。 Maven 工件 com.h2database:h2:1.4.192
必须在类路径中。
首先定义方法insert
和retrieve
:
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
方法在单引号中使用 LocalDate
的 toString
值,因此 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));
}
}
然后打印如下:
Inserted: 2015-05-20 Retrieved: 2015-05-20 Retrieved: 2015-05-19 Retrieved: 2015-05-18
如何编写 retrieve
方法,使其 returns 与 无条件 插入的值相同,假设数据库 table没有改变?
我刚刚尝试对您的 retrieve
方法进行以下修改,它对我有用:
H2 documentation for the DATE Type说的是
The date data type. The format is 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
以下解决方案也有效。我更喜欢在接受的答案中通过 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
进行的转换: