如何使用 H2 进行 JUnit-Test 防止日期更改? [英] How to prevent date change using H2 for a JUnit-Test?

查看:48
本文介绍了如何使用 H2 进行 JUnit-Test 防止日期更改?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于使用 H2 的 JUnit-Test,我保存了一个具有 LocalDate 属性和值 LocalDate.parse("1900-01-01") 的实体.测试失败

 预期:是 <1900-01-01>但是:是<1899-12-31>

LocalDate 不关心时区,所以我猜更改是在 H2 数据库的保存操作期间进行的.

我已经尝试设置时区 UTC,因为那应该是 H2 的时区,然后 H2 不应将其移交的日期解释为具有另一个必须转换的时区到 UTC.但这似乎没有帮助.

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

预先感谢您的建议和想法

解决方案

无法重现您的测试失败

您的代码:

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

...按照文档工作,产生 1900 年 1 月 1 日的值.

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

查看

For a JUnit-Test using H2 I am saving an entity with a LocalDate property and the value LocalDate.parse("1900-01-01"). The test fails with

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

LocalDate does not care about timezone, so I guess the change is made during the saving-action at the H2 database.

I already tried to set the timezone UTC, since that should be the timezone for the H2 and then the H2 should not interpret the date it is handed over as having another timezone that has to be converted to UTC. But that did not seem to help.

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

Thanks in advance for suggestions and ideas

解决方案

Cannot reproduce your test failure

Your code:

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

…works as documented, producing a value of January 1, 1900.

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

See that code run live at IdeOne.com.

1900-01-01

➥ There must be more to your code than you showed us.

Example app using H2

You said:

I guess the change is made during the saving-action at the H2 database.

Let's try it. Here is an entire example app using H2 version 1.4.200. Ran from IntelliJ 2020.1 on macOS Mojave with Java 14.

We have a table event_ with an id_ column of type UUID, and a column when_ of type LocalDate. We write a single value for a value of 1900-01-01.

As you can see if you run this code, we get back a LocalDate object for which the method toString returns 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();
        }
    }
}

When that example app runs:

id_: 949830e0-77c8-49a3-8804-0972ff70ca2c | when_: 1900-01-01


Do not rely on default time zone

Your code:

TimeZone.setDefault(TimeZone.getTimeZone("UTC"));

…is quite risky at deployment. Setting the time zone immediately affects all code in all threads of all apps running within that JVM. You could be pulling out the carpet from beneath the feet of other apps, or ever other parts of your own app.

➥ Better to never rely on the JVM’s current default time zone.

To capture the current moment as seen in UTC, simply call Instant.now. An Instant represents a moment in UTC with a resolution of nanoseconds. This class replaces java.util.Date.

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

For database work, use the more flexible class OffsetDateTime. The JDBC 4.2 specification inexplicably requires support for OffsetDateTime but not the more commonly used Instant and ZonedDateTime.

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

Write that to the database.

myPreparedStatement.setObject( … , odt ) ;

Retrieval.

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

When recording a moment (a specific point on the timeline) such as that represented by OffsetDateTime, your column in the database must be of a data type akin to the SQL standard type TIMESTAMP WITH TIME ZONE (not WITHOUT). In H2, the data type to use has that very name, TIMESTAMP WITH TIME ZONE.

这篇关于如何使用 H2 进行 JUnit-Test 防止日期更改?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆