Hibernate 6: how to customize JSON field serializer to support LocalDateTime. Spring Boot 3 config

Elvis Ciotti
2 min readJan 12, 2023

The new Hibernate 6 supports JSON type natively, without using other libraries or custom attribute converters. Nevertheless, there are no specific options to configure how you want to serialize, e.g. you might want to serialize a LocalDateTime or just customize the Jackson options.

Hibernate serializes using FormatMapper instances, and the default is JacksonJsonFormatMapper, which basically uses a Jackson instance constructed without any additional options.

As written in the hibernate docs, you can luckily configure the serializing class (as long as it implements the FormatMapper interface) with the `hibernate.type.json_format_mapper` option.

If you are using Spring boot, you can add that via JPA properties

 spring.jpa.properties.hibernate.type.json_format_mapper=io.github.elvisciotti.JacksonJsonFormatMapperCustom

And then you define the class (not a bean) that implements the interface. In my case, I was happy with Jackson, but I just wanted to customize its options, so I ended up decorating JacksonJsonFormatMapperby passing my custom Jackson instance. Very simple. See the code below:

package io.github.elvisciotti.springProject.config.hibernate;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.hibernate.type.FormatMapper;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.jackson.JacksonJsonFormatMapper;

public class JacksonJsonFormatMapperCustom implements FormatMapper {
private final FormatMapper delegate;

public JacksonJsonFormatMapperCustom() {
// internal instance with my custom settings
ObjectMapper objectMapper = createObjectMapper();

// wrapper / decorator
delegate = new JacksonJsonFormatMapper(objectMapper);
}

private static ObjectMapper createObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper()
.registerModule(new JavaTimeModule())
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
return objectMapper;
}

@Override
public <T> T fromString(CharSequence charSequence, JavaType<T> javaType, WrapperOptions wrapperOptions) {
return delegate.fromString(charSequence, javaType, wrapperOptions);
}

@Override
public <T> String toString(T t, JavaType<T> javaType, WrapperOptions wrapperOptions) {
return delegate.toString(t, javaType, wrapperOptions);
}
}

Thanks for reading !

What to do next:
- Clap if useful
- Buy me a coffee
- Follow me for more
- Read my other articles below or from my profile
- Keep in touch on LinkedIn

--

--

Elvis Ciotti

Software Contractor — Java, Spring, k8s, AWS, Javascript @ London - hire me at https://www.linkedin.com/in/elvisciotti