How does the Hibernate JAVA_TIME_USE_DIRECT_JDBC setting work
Are you struggling with performance issues in your Spring, Jakarta EE, or Java EE application?
What if there were a tool that could automatically detect what caused performance issues in your JPA and Hibernate data access layer?
Wouldn’t it be awesome to have such a tool to watch your application and prevent performance issues during development, long before they affect production systems?
Well, Hypersistence Optimizer is that tool! And it works with Spring Boot, Spring Framework, Jakarta EE, Java EE, Quarkus, Micronaut, or Play Framework.
So, rather than fixing performance issues in your production system on a Saturday night, you are better off using Hypersistence Optimizer to help you prevent those issues so that you can spend your time on the things that you love!
Introduction
In this article, we are going to see how the Hibernate JAVA_TIME_USE_DIRECT_JDBC setting works and what JDBC Driver supports this feature.
This setting can be set programmatically, as illustrated by the following Spring Java-based configuration:
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean =
new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
⠀
...
⠀
entityManagerFactoryBean.setJpaVendorAdapter(
new HibernateJpaVendorAdapter()
);
entityManagerFactoryBean.setJpaProperties(additionalProperties());
return entityManagerFactoryBean;
}
protected Properties additionalProperties() {
Properties properties = new Properties();
properties.setProperty(
AvailableSettings.JAVA_TIME_USE_DIRECT_JDBC,
Boolean.TRUE.toString()
);
return properties;
}
Or you can set it declaratively in the Spring Boot application.properties configuration file like this:
spring.jpa.properties.hibernate.type.java_time_use_direct_jdbc=true
Domain Model
To test how the Hibernate JAVA_TIME_USE_DIRECT_JDBC setting works, we are going to use the following DateTimeEvent entity:

As I explained in this article, by default, Hibernate uses the PreparedStatement.setTimestamp(int parameterIndex, Timestamp value[, Calendar timeZone]) methods to bind the LocalDateTime, OffsetDateTime, and ZonedDateTime Object types.
However, JDBC Drivers now offer the possibility of passing these Java 8 Date Time Objects directly via the PreparedStatement.setObject(int parameterIndex, Object value, int targetSqlType) method, and by enabling the hibernate.type.java_time_use_direct_jdbc setting, Hibernate can use the setObject method instead.
How does the Hibernate JAVA_TIME_USE_DIRECT_JDBC setting work
Behind the scenes, Hibernate will use the SetObjectBinder to bind the LocalDateTime, OffsetDateTime, and ZonedDateTime Object types to the PreparedStatement when executing a DML statement:
public class SetObjectBinder<T> extends BasicBinder<T> {
⠀
private final Class<?> baseClass;
⠀
private final int jdbcTypeCode;
⠀
public SetObjectBinder(
JavaType<T> javaType, JdbcType jdbcType,
Class<?> baseClass, int jdbcTypeCode) {
super(javaType, jdbcType);
this.baseClass = baseClass;
this.jdbcTypeCode = jdbcTypeCode;
}
⠀
@Override
protected void doBind(
PreparedStatement st, T value, int index,
WrapperOptions options) throws SQLException {
st.setObject(
index,
normalize(value, options),
jdbcTypeCode
);
}
⠀
protected Object normalize(
T value, WrapperOptions options) {
return getJavaType().unwrap(
value,
baseClass,
options
);
}
⠀
...
}
The jdbcTypeCode variable holds the value of the java.sql.Types field matching the column type that is meant to store the Java Date/Time Object in question.
For the LocalDateTime, the jdbcTypeCode value is Types.TIMESTAMP.
For the OffsetDateTime and ZonedDateTime Object types, the jdbcTypeCode value is Types.TIMESTAMP_WITH_TIMEZONE.
Now, when persisting the DateTimeEvent on Oracle:
DateTimeEvent _event = new DateTimeEvent()
.setId(1L)
.setLocalDateTime(
LocalDateTime.of(2024, 6, 18, 12, 34)
)
.setOffsetDateTime(
OffsetDateTime.of(2024, 6, 18, 13, 25, 36, 0, ZoneOffset.UTC)
)
.setZonedDateTime(
ZonedDateTime.of(2024, 6, 18, 11, 22, 33, 0, ZoneOffset.UTC)
);
entityManager.persist(_event);
Hibernate will generate the following SQL INSERT statement:
Query:["
INSERT INTO date_time_event (
local_date_time,
offset_date_time,
zoned_date_time,
id)
VALUES (?,?,?,?)
"],
Params:[(
2024-06-18T12:34,
2024-06-18T13:25:36Z,
2024-06-18T11:22:33Z,
1
)]
While the Oracle JDBC Driver allows us to pass the LocalDateTime, OffsetDateTime, and ZonedDateTime Objects to the database server, other database systems might not support all these Java Data/Time API Object types.
On SQL Server, PostgreSQL, and MySQL, you cannot bind the
ZonedDateTimevalue to aTIMESTAMP_WITH_TIMEZONEcolumn.For this reason, you should not activate the
hibernate.type.java_time_use_direct_jdbcsetting if your entities haveZonedDateTimeattributes and you’re running on SQL Server, PostgreSQL, and MySQL.
To test the hibernate.type.java_time_use_direct_jdbc setting on SQL Server and PostgreSQL, we are going to use the following DateTimeEvent entity that no longer uses the ZonedDateTime attribute:
@Entity(name = "DateTimeEvent")
@Table(name = "date_time_event")
public class DateTimeEvent {
<pre><code>@Id
private Long id;
@Column(name = &quot;local_date_time&quot;)
private LocalDateTime localDateTime;
@Column(name = &quot;offset_date_time&quot;)
private OffsetDateTime offsetDateTime;
</code></pre>
}
With this change in place, when persisting the DateTimeEvent on SQL Server or PostgreSQL:
DateTimeEvent _event = new DateTimeEvent()
.setId(1L)
.setLocalDateTime(
LocalDateTime.of(2024, 6, 18, 12, 34)
)
.setOffsetDateTime(
OffsetDateTime.of(2024, 6, 18, 13, 25, 36, 0, ZoneOffset.UTC)
);
entityManager.persist(_event);
Hibernate will generate the following INSERT statement:
Query:["
INSERT INTO date_time_event (
local_date_time,
offset_date_time,
id)
VALUES (?,?,?,?)
"],
Params:[(
2024-06-18T12:34,
2024-06-18T13:25:36Z,
1
)]
The MySQL JDBC Driver 8.0.33 does not support the
Types.TIMESTAMP_WITH_TIMEZONE, so you won’t be able to use theOffsetDateTimeorZonedDateTimeas entity attributes while having thehibernate.type.java_time_use_direct_jdbcsetting enabled.
If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.
Conclusion
While the JDBC Types.TIMESTAMP_WITH_TIMEZONE has been available since Java 8, not all Drivers support it or bind the ZonedDateTime to such a column type.
Therefore, prior to enabling the new Hibernate JAVA_TIME_USE_DIRECT_JDBC setting, it would be great to check whether the current JDBC Driver you are using supports binding all Java Date/Time Objects to an SQL statement.





