Hibernate 未正确解析 DST 转换周围的值
Hibernate not parsing values around DST transitions correctly
我遇到了一个问题,在 DST 转换期间发生的 Instant
被正确地保存到数据库中,但是当回读时返回不同的值。
具体来说,我在 Europe/London
并且 2021-10-31T01:04:00Z
有问题 - 我回来的瞬间是 2021-10-31T00:04:00Z
.
我已经创建了一个简单的应用程序来演示这一点 - 它持续存在三个事件,一个在 DST 转换之前,一个是之前提到的唯一一个在 DST 转换期间,然后一个在 DST 转换之后。我希望输入和输出数据始终相同。
输出如下:
ZoneId=Europe/London
Persisting:
2021-10-30T01:04:00Z
2021-10-31T01:04:00Z
2021-11-01T01:04:00Z
Native Query:
clob1: '2021-10-30 02:04:00+01'
clob2: '2021-10-31 01:04:00+01'
clob3: '2021-11-01 01:04:00+00'
Hibernate Object:
2021-10-30T01:04:00Z
2021-10-31T00:04:00Z
2021-11-01T01:04:00Z
:
import java.text.MessageFormat;
import java.time.Instant;
import java.time.ZoneId;
import java.util.Properties;
import java.util.stream.Stream;
import org.h2.Driver;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.dialect.H2Dialect;
public class StoreData {
public static void main(final String[] args) {
System.out.println(MessageFormat.format("ZoneId={0}", ZoneId.systemDefault()));
final Properties properties = new Properties();
properties.setProperty(AvailableSettings.DIALECT, H2Dialect.class.getName());
properties.setProperty(AvailableSettings.URL, "jdbc:h2:mem:test");
properties.setProperty(AvailableSettings.DRIVER, Driver.class.getName());
try (Session session = new Configuration().setProperties(properties).addAnnotatedClass(Event.class)
.buildSessionFactory().openSession()) {
final Transaction transaction = session.beginTransaction();
// Table creation done explicitly to show types used
session.createSQLQuery("CREATE TABLE EVENT(ID BIGINT, DATE TIMESTAMP WITH TIME ZONE)").executeUpdate();
System.out.println("Persisting:");
Stream.of("2021-10-30T01:04:00Z", "2021-10-31T01:04:00Z", "2021-11-01T01:04:00Z").map(Instant::parse)
.forEach(instant -> {
System.out.println(instant);
final Event e = new Event();
e.setDate(instant);
e.setId(instant.toEpochMilli());
session.persist(e);
});
transaction.commit();
System.out.println();
System.out.println("Native Query:");
session.createNativeQuery("SELECT CAST(date as TEXT) FROM event").getResultList()
.forEach(System.out::println);
System.out.println();
System.out.println("Hibernate Objects:");
session.getEntityManagerFactory().createEntityManager().createQuery("FROM Event", Event.class)
.getResultList().stream().map(Event::getDate).forEach(System.out::println);
}
}
}
:
import java.time.Instant;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class Event {
@Id
private long id;
private Instant date;
public long getId() {
return id;
}
public void setId(final long id) {
this.id = id;
}
public Instant getDate() {
return date;
}
public void setDate(final Instant date) {
this.date = date;
}
}
pom.xml
:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.me</groupId>
<artifactId>help</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.6.7.Final</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.1.210</version>
</dependency>
</dependencies>
</project>
有趣的是,如果我迁移到 Hibernate v6,它看起来能正常工作吗?
ZoneId=Europe/London
Persisting:
2021-10-30T01:04:00Z
2021-10-31T01:04:00Z
2021-11-01T01:04:00Z
Native Query:
clob1: '2021-10-30 01:04:00+00'
clob2: '2021-10-31 01:04:00+00'
clob3: '2021-11-01 01:04:00+00'
Hibernate Objects:
2021-10-30T01:04:00Z
2021-10-31T01:04:00Z
2021-11-01T01:04:00Z
明确地说:
Europe/London
时区,
H2 内存数据库。
我通过将 AvailableSettings.JDBC_TIME_ZONE
设置为 UTC
并将列类型更改为 TIMEZONE
.
来完成此工作
我相信这是 Hibernate v5 和 H2 驱动程序之间的不兼容。
我遇到了一个问题,在 DST 转换期间发生的 Instant
被正确地保存到数据库中,但是当回读时返回不同的值。
具体来说,我在 Europe/London
并且 2021-10-31T01:04:00Z
有问题 - 我回来的瞬间是 2021-10-31T00:04:00Z
.
我已经创建了一个简单的应用程序来演示这一点 - 它持续存在三个事件,一个在 DST 转换之前,一个是之前提到的唯一一个在 DST 转换期间,然后一个在 DST 转换之后。我希望输入和输出数据始终相同。
输出如下:
ZoneId=Europe/London
Persisting:
2021-10-30T01:04:00Z
2021-10-31T01:04:00Z
2021-11-01T01:04:00Z
Native Query:
clob1: '2021-10-30 02:04:00+01'
clob2: '2021-10-31 01:04:00+01'
clob3: '2021-11-01 01:04:00+00'
Hibernate Object:
2021-10-30T01:04:00Z
2021-10-31T00:04:00Z
2021-11-01T01:04:00Z
:
import java.text.MessageFormat;
import java.time.Instant;
import java.time.ZoneId;
import java.util.Properties;
import java.util.stream.Stream;
import org.h2.Driver;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.dialect.H2Dialect;
public class StoreData {
public static void main(final String[] args) {
System.out.println(MessageFormat.format("ZoneId={0}", ZoneId.systemDefault()));
final Properties properties = new Properties();
properties.setProperty(AvailableSettings.DIALECT, H2Dialect.class.getName());
properties.setProperty(AvailableSettings.URL, "jdbc:h2:mem:test");
properties.setProperty(AvailableSettings.DRIVER, Driver.class.getName());
try (Session session = new Configuration().setProperties(properties).addAnnotatedClass(Event.class)
.buildSessionFactory().openSession()) {
final Transaction transaction = session.beginTransaction();
// Table creation done explicitly to show types used
session.createSQLQuery("CREATE TABLE EVENT(ID BIGINT, DATE TIMESTAMP WITH TIME ZONE)").executeUpdate();
System.out.println("Persisting:");
Stream.of("2021-10-30T01:04:00Z", "2021-10-31T01:04:00Z", "2021-11-01T01:04:00Z").map(Instant::parse)
.forEach(instant -> {
System.out.println(instant);
final Event e = new Event();
e.setDate(instant);
e.setId(instant.toEpochMilli());
session.persist(e);
});
transaction.commit();
System.out.println();
System.out.println("Native Query:");
session.createNativeQuery("SELECT CAST(date as TEXT) FROM event").getResultList()
.forEach(System.out::println);
System.out.println();
System.out.println("Hibernate Objects:");
session.getEntityManagerFactory().createEntityManager().createQuery("FROM Event", Event.class)
.getResultList().stream().map(Event::getDate).forEach(System.out::println);
}
}
}
:
import java.time.Instant;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class Event {
@Id
private long id;
private Instant date;
public long getId() {
return id;
}
public void setId(final long id) {
this.id = id;
}
public Instant getDate() {
return date;
}
public void setDate(final Instant date) {
this.date = date;
}
}
pom.xml
:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.me</groupId>
<artifactId>help</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.6.7.Final</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.1.210</version>
</dependency>
</dependencies>
</project>
有趣的是,如果我迁移到 Hibernate v6,它看起来能正常工作吗?
ZoneId=Europe/London
Persisting:
2021-10-30T01:04:00Z
2021-10-31T01:04:00Z
2021-11-01T01:04:00Z
Native Query:
clob1: '2021-10-30 01:04:00+00'
clob2: '2021-10-31 01:04:00+00'
clob3: '2021-11-01 01:04:00+00'
Hibernate Objects:
2021-10-30T01:04:00Z
2021-10-31T01:04:00Z
2021-11-01T01:04:00Z
明确地说:
Europe/London
时区,
H2 内存数据库。
我通过将 AvailableSettings.JDBC_TIME_ZONE
设置为 UTC
并将列类型更改为 TIMEZONE
.
我相信这是 Hibernate v5 和 H2 驱动程序之间的不兼容。