如何使用 H2 进行 JUnit 测试来防止日期更改?

2024-04-15

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

    Expected: is <1900-01-01>
    but: was <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 ) ;

看到那个代码在 IdeOne.com 上实时运行 https://ideone.com/SzzeJ2.

1900-01-01

➥ 您的代码一定比您向我们展示的更多。

使用 H2 的示例应用程序

你说:

我猜想更改是在 H2 数据库的保存操作期间进行的。

我们来试试吧。这是使用 H2 版本 1.4.200 的完整示例应用程序。在 macOS Mojave 上使用 Java 14 从 IntelliJ 2020.1 运行。

我们有一张桌子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-0972ff70ca2c |当_:1900-01-01


不要依赖默认时区

你的代码:

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

……部署时风险很大。设置时区会立即影响该 JVM 中运行的所有应用程序的所有线程中的所有代码。您可能会从其他应用程序或您自己的应用程序的其他部分中拔出地毯。

➥ 最好不要依赖 JVM 当前的默认时区。

要捕获 UTC 中的当前时刻,只需调用Instant.now. An Instant表示 UTC 中的一个时刻,分辨率为纳秒。这个类取代了java.util.Date.

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

对于数据库工作,使用更灵活的类OffsetDateTime。 JDBC 4.2 规范莫名其妙地需要支持OffsetDateTime但不是更常用的Instant and ZonedDateTime.

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

将其写入数据库。

myPreparedStatement.setObject( … , odt ) ;

恢复。

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

记录某个时刻(时间轴上的特定点)时,例如由OffsetDateTime,数据库中的列必须是类似于 SQL 标准类型的数据类型TIMESTAMP WITH TIME ZONE (not WITHOUT)。在 H2 中,要使用的数据类型就是这个名字,TIMESTAMP WITH TIME ZONE https://h2database.com/html/datatypes.html#timestamp_with_time_zone_type.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用 H2 进行 JUnit 测试来防止日期更改? 的相关文章

随机推荐