Как предотвратить изменение даты с помощью H2 для JUnit-Test? - PullRequest
3 голосов
/ 16 апреля 2020

Для JUnit-Test с использованием H2 я сохраняю сущность со свойством LocalDate и значением LocalDate.parse("1900-01-01"). Тест не пройден с

    Expected: is <1900-01-01>
    but: was <1899-12-31>

LocalDate не заботится о часовом поясе, поэтому я думаю, что изменение было сделано во время действия сохранения в базе данных H2.

Я уже пытался установить часовой пояс UTC, поскольку это должен быть часовой пояс для H2, а затем H2 не должен интерпретировать дату, когда он передается, как имеющий другой часовой пояс, который должен быть преобразован в UT C. Но это, похоже, не помогло.

    public static void main(final String[] args) {
        TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
        SpringApplication.run(BackendApplication.class, args);
    }

Заранее спасибо за предложения и идеи

1 Ответ

1 голос
/ 17 апреля 2020

Невозможно воспроизвести ваш тестовый сбой

Ваш код:

LocalDate.parse( "1900-01-01" )

… работает в соответствии с документацией, выдавая значение 1 января 1900 года.

LocalDate ld = LocalDate.parse("1900-01-01") ;
System.out.println( ld ) ;

См. код запускается на IdeOne.com .

1900-01-01

must В вашем коде должно быть больше, чем вы нам показали .

Пример приложения с использованием H2

Вы сказали:

Полагаю, что изменение сделано во время действия сохранения в базе данных H2.

Давайте попробуем. Вот целый пример приложения с использованием H2 версии 1.4.200. Запускается из IntelliJ 2020.1 в macOS Mojave с Java 14.

У нас есть таблица event_ с id_ столбцом типа UUID и столбцом when_ типа LocalDate. Мы записываем одно значение для значения 1900-01-01.

Как вы можете видеть, если вы запустите этот код, мы получим LocalDate объект, для которого метод toString возвращает 1900-01-01.

package work.basil.example;

import org.h2.jdbcx.JdbcDataSource;

import java.sql.*;
import java.time.LocalDate;
import java.util.Objects;
import java.util.UUID;

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

    private void doIt ( )
    {
        JdbcDataSource dataSource = Objects.requireNonNull( new JdbcDataSource() );  // Implementation of `DataSource` bundled with H2.
        dataSource.setURL( "jdbc:h2:mem:localdate_example_db;DB_CLOSE_DELAY=-1" ); // Set `DB_CLOSE_DELAY` to `-1` to keep in-memory database in existence after connection closes.
        dataSource.setUser( "scott" );
        dataSource.setPassword( "tiger" );

        String sql = null;

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

            // Insert row.
            sql = "INSERT INTO event_ ( when_ ) " + "VALUES ( ? ) ;";
            try (
                    PreparedStatement preparedStatement = conn.prepareStatement( sql ) ;
            )
            {
                LocalDate localDate = LocalDate.parse( "1900-01-01" );
                preparedStatement.setObject( 1 , localDate );  // Yesterday.
                preparedStatement.executeUpdate();
            }

            // Query all.
            sql = "SELECT * FROM event_ ;";
            try (
                    Statement stmt = conn.createStatement() ;
                    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( "when_" , LocalDate.class );  // Ditto, pass class for type-safety.

                    //Display values
                    System.out.println( "id_: " + id + " | when_: " + localDate );
                }
            }
        }
        catch ( SQLException e )
        {
            e.printStackTrace();
        }
    }
}

Когда приложение этого примера запускается :

id_: 949830e0-77c8-49a3-8804-0972ff70ca2 c | когда_: 1900-01-01


Не полагаться на часовой пояс по умолчанию

Ваш код:

TimeZone.setDefault (TimeZone. getTimeZone ("UT C"));

… довольно рискован при развертывании. Установка часового пояса немедленно влияет на весь код во всех потоках всех приложений, работающих в этой JVM. Возможно, вы вытаскиваете ковер из-под ног других приложений или других частей вашего собственного приложения.

➥ Лучше никогда не полагаться на текущий часовой пояс JVM по умолчанию.

Чтобы зафиксировать текущий момент, показанный в UT C, просто позвоните Instant.now. Instant представляет момент в UT C с разрешением наносекунд. Этот класс заменяет java.util.Date.

Instant instant = Instant.now() ;  // Capture the current moment in UTC.

Для работы с базой данных используйте более гибкий класс OffsetDateTime. Спецификация JDB C 4.2 необъяснимо требует поддержки OffsetDateTime, но не более широко используемых Instant и ZonedDateTime.

OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC ) ;  // Capture the current moment in UTC.

Запишите это в базу данных.

myPreparedStatement.setObject( … , odt ) ;

Извлечение.

OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;

При записи момента (точная c точка на временной шкале), например, представленного OffsetDateTime, ваш столбец в базе данных должен иметь тип данных, близкий к SQL стандартный тип TIMESTAMP WITH TIME ZONE ( не WITHOUT). Во H2, тип данных для использования имеет то же имя, TIMESTAMP WITH TIME ZONE.

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

...