2016-06-12 17 views
3

Ich möchte eine LocalDate in einer DATE Spalte speichern und unverändert abrufen. Sowohl DATE als auch LocalDate sind per Definition "lokale" Typen. Daher sollte das Konzept der Zeitzone in keiner Weise stören.So ordnen Sie SQL DATE LocalDate zu

Der folgende Code ist ein minimales Beispiel, das eine Tabelle mit einer Spalte DATE in einer In-Memory-Datenbank erstellt. Das Mavenartefakt com.h2database:h2:1.4.192 muss im Klassenpfad sein.

Zunächst definieren Methoden insert und 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"); 
    } 
} 

Beachten Sie, dass die insert Methode den toString Wert des LocalDate in einfache Anführungszeichen verwendet, so gibt es keine Möglichkeit für Java ™ Zeitzone Mehrdeutigkeit zu erstellen. Jetzt insert einmal anrufen und dann mehrmals retrieve mit unterschiedlichen timzone Einstellungen jedes Mal: ​​

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

Dann folgt das gedruckt wird:

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

Wie die retrieve Methode zu schreiben, so dass es den gleichen Wert zurückgibt das wurde bedingungslos eingefügt, unter der Annahme, dass die Datenbanktabelle nicht ändert?

+0

Sie java.util.Date importierende? Auf dieser 'requested =' Zeile verwenden Sie das vollqualifizierte 'java.sql.Date' auf der linken Seite der Zuweisung, aber nicht das Recht. –

+1

Warum haben Sie erwartet, dass das 3 gleiche 'LocalDate' gedruckt wird? Sie fügen das Datum "2015-05-20" ein. Wenn Sie dann die Standardzeitzone ändern, gibt die Methode 'java.sql.Date.toLocalDate' ein anderes Ergebnis zurück. – Tunaki

+0

@BasilBourque behoben –

Antwort

3

Ich habe gerade versucht, die folgenden Änderungen an Ihrem retrieve Methode und es funktionierte für mich:

Die H2 documentation for the DATE Type sagt, dass es

Das Datum Datentyp ist. Das Format ist JJJJ-MM-TT.

Anstatt also Ihre ...

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

... Ich habe gerade verwendet ...

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

... und mein Code erzeugt

Inserted: 2015-05-20 
Retrieved: 2015-05-20 
Retrieved: 2015-05-20 
Retrieved: 2015-05-20 
+0

Sieht sicher für mich aus, außer vielleicht Null Handhabung. Vielen Dank! Ich frage mich immer noch, wie ich es mit Oracle DB anstelle von H2 machen soll (welches die Genauigkeit einer Sekunde in einem 'DATE' speichert). Das wäre eine andere Frage. –

1

Die folgende Lösung funktioniert auch. Ich bevorzuge die Konvertierung über String in die angenommene Antwort, weil es das unten gezeigte Zeitzonen-Basteln vermeidet. Es kann jedoch nicht in allen Datenbanken auf die gleiche Weise funktionieren, da einige, z. Oracle, haben eine andere Definition von 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"); 
} 

Die Umwandlung über java.util.Date wird in dieser Frage dargelegt, wie durch den Benutzer Tunaki vorgeschlagen: Missed opportunity to fix JDBC date handling in Java 8?