Spring Boot 3 (Hibernate 6.1) in combination with PostgreSQL seems to have a problem with Instant
values mapped to SQL timestamp
s (without time zone
), as the following test case fails. Values read differ from the written values by the amount of the local time zone offset.
The test case executes successfully when using the H2 test database or when switching back to Spring Boot 2.7.6 (Hibernate 5.6).
JPA entity:
@Entity
public class MyEntity {
@Id
UUID id = UUID.randomUUID();
//@JdbcType(TimestampJdbcType.class) //work-around to get it somehow working
//(alternative: declare this globally in a custom Hibernate dialect)
//but why is this neccessary only with spring-boot-3 and postgres?
@Column(columnDefinition = "timestamp")
Instant createdTimestamp = Instant.now();
}
Spring Data repository:
public interface MyEntityRepository extends CrudRepository<MyEntity, UUID> {
}
JUnit integration test:
@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE) //comment out to use the H2 database
class MyEntityRepositoryITest {
@Autowired
MyEntityRepository myEntityRepository;
@Test
@Transactional(propagation = NOT_SUPPORTED) //do not use a transaction
void readInstantSameAsPersisted() {
var entity = new MyEntity();
myEntityRepository.save(entity);
var read = myEntityRepository.findById(entity.id).orElseThrow();
assertEquals(entity.createdTimestamp.getEpochSecond(), read.createdTimestamp.getEpochSecond());
}
}
Maven pom.xml:
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
Spring application.properties:
spring.jpa.hibernate.ddl-auto=create
spring.datasource.url=jdbc:postgresql://localhost:5432/dbname
spring.datasource.username=username
spring.datasource.password=password
Notes:
- Setting
spring.jpa.properties.hibernate.jdbc.time_zone
to any value doesn't help. - I don't see switching to
LocalDateTime
as an option, as I want thecreatedTimestamp
to represent an unambiguous point in time. - I don't see switching to a SQL type with zone information as an option, as I don't want to allow values with varying zones in the database.
spring.datasource.hikari.connection-init-sql=SET TIME ZONE 'UTC'
also works as a work-around, but I don't see why this is neccessary -- reading a value that is based on seconds since 1970-01-01 should give the same result as the written value, regardless of which zone is used behind the scenes.- pgJDBC does not support
Instant
, but Hibernate does. - This answer could be related, but then why does it work with the older Hibernate?
Update
This answer says timestamp with time zone
does (unlike the name suggests) in fact not carry additional zone information and just "stores a point on the UTC time line" that can be represented in different zones, which seems like a better fit for my use case.