web-dev-qa-db-ja.com

Java.time.LocalDateオブジェクトをH2などのSQLデータベースとの間で挿入およびフェッチします

挿入およびフェッチの方法 Java.timeLocalDate などのタイプ [〜#〜] jdbc [〜#〜]H2 Database Engine ?などのSQLデータベースに

PreparedStatement::setDate および ResultSet::getDate を使用する古い方法は、レガシー Java.sql.Date タイプで機能します。これらの面倒な古い日時クラスの使用は避けたいと思います。

JDBCドライバー を介してJava.time型を送信するための最新の方法は何ですか?

7
Basil Bourque

JDBCを介してJava.timeオブジェクトを交換する方法は2つあります。

  • JDBC 4.2準拠のドライバー
    JDBCドライバーが JDBC4.2仕様 以降に準拠している場合は、Java.timeオブジェクトを直接処理できます。
  • JDBC 4.2より前の古いドライバー
    JDBCドライバーがまだJDBC4.2以降に準拠していない場合は、Java.timeオブジェクトを同等のJava.sqlタイプに、またはその逆に簡単に変換します。古いクラスに追加された新しい変換メソッドを探してください。

Java.util.DateJava.util.Calendarなどの従来の日時クラス、およびJava.sqlなどの関連するJava.sql.Dateクラスはひどい混乱です。不十分に設計されたハッキン​​グされたアプローチで構築されており、欠陥があり、面倒で、混乱を招くことが証明されています。可能な限りそれらを避けてください。現在、Java.timeクラスに取って代わられています。

Table of date-time types in Java (both legacy and modern) and in standard SQL

JDBC4.2準拠のドライバー

H2用の組み込みJDBCドライバー(2017-03現在)はJDBC4.2に準拠しているようです。

準拠ドライバーは、Java.timeタイプを認識するようになりました。しかし、JDBC委員会はsetLocalDate/getLocalDateの種類のメソッドを追加するのではなく、 setObjectgetObject メソッド。

データベースにデータを送信するには、Java.timeオブジェクトをPreparedStatement::setObjectに渡すだけです。渡された引数のJavaタイプはドライバーによって検出され、適切なSQLタイプに変換されます。AJava LocalDateはSQL DATEタイプ。これらのマッピングのリストについては、 JDBC Maintenance Release 4.2 PDFドキュメントのセクション22を参照してください。

myPreparedStatement.setObject ( 1 , myLocalDate ); // Automatic detection and conversion of data type.

データベースからデータを取得するには、ResultSet::getObjectを呼び出します。結果のObjectオブジェクトをキャストする代わりに、受け取ると予想されるデータ型の Class という追加の引数を渡すことができます。期待されるクラスを指定することにより、 type-safety[〜#〜] ide [〜#〜] およびコンパイラによってチェックおよび検証されます。

LocalDate localDate = myResultSet.getObject ( "my_date_column_" , LocalDate.class ); 

これは、H2データベースにLocalDate値を挿入して選択する方法を示す実用的なサンプルアプリ全体です。

package com.example.h2localdate;

import Java.sql.*;
import Java.time.LocalDate;
import Java.time.ZoneId;
import Java.util.UUID;

/**
 * Hello world!
 */
public class App {
    public static void main ( String[] args ) {
        App app = new App ( );
        app.doIt ( );
    }

    private void doIt ( ) {
        try {
            Class.forName ( "org.h2.Driver" );
        } catch ( ClassNotFoundException e ) {
            e.printStackTrace ( );
        }

        try (
            Connection conn = DriverManager.getConnection ( "jdbc:h2:mem:trash_me_db_" ) ;
            Statement stmt = conn.createStatement ( ) ;
        ) {
            String tableName = "test_";
            String sql = "CREATE TABLE " + tableName + " (\n" +
                "  id_ UUID DEFAULT random_uuid() PRIMARY KEY ,\n" +
                "  date_ DATE NOT NULL\n" +
                ");";
            stmt.execute ( sql );

            // Insert row.
            sql = "INSERT INTO test_ ( date_ ) " + "VALUES (?) ;";
            try ( PreparedStatement preparedStatement = conn.prepareStatement ( sql ) ; ) {
                LocalDate today = LocalDate.now ( ZoneId.of ( "America/Montreal" ) );
                preparedStatement.setObject ( 1, today.minusDays ( 1 ) );  // Yesterday.
                preparedStatement.executeUpdate ( );
                preparedStatement.setObject ( 1, today );                  // Today.
                preparedStatement.executeUpdate ( );
                preparedStatement.setObject ( 1, today.plusDays ( 1 ) );   // Tomorrow.
                preparedStatement.executeUpdate ( );
            }

            // Query all.
            sql = "SELECT * FROM test_";
            try ( ResultSet rs = stmt.executeQuery ( sql ) ; ) {
                while ( rs.next ( ) ) {
                    //Retrieve by column name
                    UUID id = rs.getObject ( "id_", UUID.class );  // Pass the class to be type-safe, rather than casting returned value.
                    LocalDate localDate = rs.getObject ( "date_", LocalDate.class );  // Ditto, pass class for type-safety.

                    //Display values
                    System.out.println ( "id_: " + id + " | date_: " + localDate );
                }
            }

        } catch ( SQLException e ) {
            e.printStackTrace ( );
        }
    }
}

実行時。

id_:e856a305-41a1-45fa-ab69-cfa676285461 |日付_:2017-03-26

id_:a4474e79-3e1f-4395-bbba-044423b37b9f |日付_:2017-03-27

id_:5d47bc3d-ebfa-43ab-bbc2-7bb2313b33b0 |日付_:2017-03-28

非準拠のドライバー

H2の場合、上記のコードは私が推奨する道です。ただし、参考までに、JDBC 4.2にまだ準拠していない他のデータベースについては、Java.timeタイプとJava.sqlタイプを簡単に変換する方法を紹介します。この種の変換コードは、以下に示すように確かにH2で実行されますが、上に示したより単純なアプローチがあるため、これを行うのはばかげています。

データベースにデータを送信するには、その古いクラスに追加された新しいメソッドを使用して、LocalDateJava.sql.Date オブジェクトに変換します。

Java.sql.Date mySqlDate = Java.sql.Date.valueOf( myLocalDate );

次に、PreparedStatement::setDateメソッドに渡します。

preparedStatement.setDate ( 1, mySqlDate );

データベースから取得するには、ResultSet::getDateを呼び出してJava.sql.Dateオブジェクトを取得します。

Java.sql.Date mySqlDate = myResultSet.getDate( 1 );

次に、すぐにLocalDateに変換します。 Java.sqlオブジェクトはできるだけ簡単に処理する必要があります。 Java.time型のみを使用して、すべてのビジネスロジックおよびその他の作業を実行します。

LocalDate myLocalDate = mySqlDate.toLocalDate();

これは、H2データベースでのJava.timeタイプでのJava.sqlタイプの使用を示すサンプルアプリ全体です。

package com.example.h2localdate;

import Java.sql.*;
import Java.time.LocalDate;
import Java.time.ZoneId;
import Java.util.UUID;

/**
 * Hello world!
 */
public class App {
    public static void main ( String[] args ) {
        App app = new App ( );
        app.doIt ( );
    }

    private void doIt ( ) {
        try {
            Class.forName ( "org.h2.Driver" );
        } catch ( ClassNotFoundException e ) {
            e.printStackTrace ( );
        }

        try (
            Connection conn = DriverManager.getConnection ( "jdbc:h2:mem:trash_me_db_" ) ;
            Statement stmt = conn.createStatement ( ) ;
        ) {
            String tableName = "test_";
            String sql = "CREATE TABLE " + tableName + " (\n" +
                "  id_ UUID DEFAULT random_uuid() PRIMARY KEY ,\n" +
                "  date_ DATE NOT NULL\n" +
                ");";
            stmt.execute ( sql );

            // Insert row.
            sql = "INSERT INTO test_ ( date_ ) " + "VALUES (?) ;";
            try ( PreparedStatement preparedStatement = conn.prepareStatement ( sql ) ; ) {
                LocalDate today = LocalDate.now ( ZoneId.of ( "America/Montreal" ) );
                preparedStatement.setDate ( 1, Java.sql.Date.valueOf ( today.minusDays ( 1 ) ) );  // Yesterday.
                preparedStatement.executeUpdate ( );
                preparedStatement.setDate ( 1, Java.sql.Date.valueOf ( today ) );  // Today.
                preparedStatement.executeUpdate ( );
                preparedStatement.setDate ( 1, Java.sql.Date.valueOf ( today.plusDays ( 1 ) ) );  // Tomorrow.
                preparedStatement.executeUpdate ( );
            }

            // Query all.
            sql = "SELECT * FROM test_";
            try ( ResultSet rs = stmt.executeQuery ( sql ) ; ) {
                while ( rs.next ( ) ) {
                    //Retrieve by column name
                    UUID id = ( UUID ) rs.getObject ( "id_" );  // Cast the `Object` object to UUID if your driver does not support JDBC 4.2 and its ability to pass the expected return type for type-safety.
                    Java.sql.Date sqlDate = rs.getDate ( "date_" );
                    LocalDate localDate = sqlDate.toLocalDate ();  // Immediately convert into Java.time. Mimimize use of Java.sql types.

                    //Display values
                    System.out.println ( "id_: " + id + " | date_: " + localDate );
                }
            }

        } catch ( SQLException e ) {
            e.printStackTrace ( );
        }
    }
}

楽しみのために別のものを試してみましょう。今回は 接続を取得するためのDataSource実装を使用 。そして今回は LocalDate.MIN を試してみました。これは、ISO 8601、-999999999-01-01で約10億年前の定数です。

package work.basil.example;

import Java.sql.*;
import Java.time.LocalDate;
import Java.time.ZoneId;
import Java.util.UUID;

public class LocalDateMin
{
    public static void main ( String[] args )
    {
        LocalDateMin app = new LocalDateMin();
        app.doIt();
    }

    private void doIt ()
    {
        org.h2.jdbcx.JdbcDataSource ds = new org.h2.jdbcx.JdbcDataSource();
        ds.setURL( "jdbc:h2:mem:localdate_min_example_db_;DB_CLOSE_DELAY=-1" );
        ds.setUser( "scott" );
        ds.setPassword( "tiger" );

        try (
                Connection conn = ds.getConnection() ;
                Statement stmt = conn.createStatement() ;
        )
        {
            String tableName = "test_";
            String sql = "CREATE TABLE " + tableName + " (\n" +
                    "  id_ UUID DEFAULT random_uuid() PRIMARY KEY ,\n" +
                    "  date_ DATE NOT NULL\n" +
                    ");";
            stmt.execute( sql );

            // Insert row.
            sql = "INSERT INTO test_ ( date_ ) " + "VALUES (?) ;";
            try ( PreparedStatement preparedStatement = conn.prepareStatement( sql ) ; )
            {
                LocalDate today = LocalDate.now( ZoneId.of( "America/Montreal" ) );
                preparedStatement.setObject( 1 , LocalDate.MIN );  // MIN =
                preparedStatement.executeUpdate();
            }

            // Query all.
            sql = "SELECT * FROM test_";
            try ( ResultSet rs = stmt.executeQuery( sql ) ; )
            {
                while ( rs.next() )
                {
                    //Retrieve by column name
                    UUID id = rs.getObject( "id_" , UUID.class );  // Pass the class to be type-safe, rather than casting returned value.
                    LocalDate localDate = rs.getObject( "date_" , LocalDate.class );  // Ditto, pass class for type-safety.

                    //Display values
                    System.out.println( "id_: " + id + " | date_: " + localDate );
                }
            }

        } catch ( SQLException e )
        {
            e.printStackTrace();
        }
    }
}

id_:4b0ba138-d7ae-469b-854f-5cbe7430026f | date _:-999999999-01-01


Java.timeについて

Java.time フレームワークはJava 8以降に組み込まれています。これらのクラスは、厄介な古い legacy date-timeクラスに取って代わります。 Java.util.DateCalendar 、& SimpleDateFormat

Joda-Time プロジェクトは現在 メンテナンスモード であり、 Java.time クラスへの移行をアドバイスします。

詳細については、 Oracleチュートリアル を参照してください。そして、StackOverflowで多くの例と説明を検索してください。仕様は JSR 31 です。

Java.timeクラスはどこで入手できますか?

ThreeTen-Extra プロジェクトは、追加のクラスでJava.timeを拡張します。このプロジェクトは、Java.timeに将来追加される可能性のある試験場です。 IntervalYearWeekYearQuarter などの便利なクラスがここにあります。 、および more

12
Basil Bourque