I am migrating to quarkus 3 and i couldnt find a way to serialize properly (like in hibernate 5) my OffsetDateTime for a field annotated with jsonb in an entity :
This is one of my entity, OffsetDateTime is not a problem here when persisting because i added the parameter hibernate-orm.mapping.timezone.default-storage="NORMALIZE" in configuration so i get my date right with the timezone
package myPackage;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.orange.erable.ohp.common.quarkus.rest.internal.identifier.models.IdentifierCharacteristicNameDTO;
import com.orange.erable.ohp.persistence.entities.enums.EIdentifierCategory;
import com.orange.erable.ohp.persistence.entities.enums.EIdentifierStatus;
import io.hypersistence.utils.hibernate.type.basic.PostgreSQLEnumType;
import io.quarkus.hibernate.orm.panache.PanacheEntityBase;
import lombok.Data;
import org.hibernate.annotations.Type;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.Transient;
import jakarta.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.time.OffsetDateTime;
import java.util.UUID;
@Data
@Entity
@Table(name = "t_identifier")
public class IdentifierEntity extends PanacheEntityBase {
@Id
private String idohp;
private String msisdn;
private String idAdv;
private String contractAid;
private String countryCode;
@NotNull
@Enumerated(EnumType.STRING)
@Type(PostgreSQLEnumType.class)
@Column(columnDefinition = "status")
private EIdentifierStatus status;
@Enumerated(EnumType.STRING)
@Type(PostgreSQLEnumType.class)
@Column(columnDefinition = "category")
private EIdentifierCategory category;
@Column(updatable = false)
private OffsetDateTime creationDate = OffsetDateTime.now();
private OffsetDateTime updateDate = OffsetDateTime.now();
@JsonInclude(JsonInclude.Include.NON_NULL)
@Transient
private boolean updated = false;
@JsonInclude(JsonInclude.Include.NON_NULL)
@Transient
private boolean created = false;
}
But for this entity which includes previous entity, the OffsetDateTime inside identifier are not persisted with the right format, it is persisted with UTC format, im missing my timezone:
package myPackage;
import io.hypersistence.utils.hibernate.type.json.JsonBinaryType;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.*;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.time.OffsetDateTime;
@Data
@NoArgsConstructor
@Entity
@Table(name = "t_identifier_history")
public class IdentifierHistoryEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String idohp;
@Type(JsonBinaryType.class)
@Column(columnDefinition = "jsonb")
private IdentifierEntity identifier;
@Column(insertable = false, updatable = false)
private OffsetDateTime creationDate = OffsetDateTime.now();
@Column(insertable = false)
private OffsetDateTime updateDate = OffsetDateTime.now();
private String historyVersion;
public IdentifierHistoryEntity(IdentifierEntity identifierEntity, String historyVersion) {
this.idohp = identifierEntity.getIdohp();
this.identifier = identifierEntity;
this.historyVersion = historyVersion;
}
}
I have tried setting @JdbcTypeCode( SqlTypes.JSON ) on identifier field
I have tried to create my own format mapper and refer to it with quarkus.hibernate-orm.unsupported-properties."hibernate.type.json_format_mapper"="myCustomFormatMapper" Here is the format mapper :
public class RegisterJacksonCustomizer implements FormatMapper {
private final FormatMapper delegate = new JacksonJsonFormatMapper(createObjectMapper());
private static ObjectMapper createObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL);
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
objectMapper.disable(SerializationFeature.WRAP_ROOT_VALUE);
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
final JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(OffsetDateTime.class, new CustomOffsetDateTimeSerializer(DATE_TIME_FORMATTER));
javaTimeModule.addDeserializer(OffsetDateTime.class, new CustomOffsetDateTimeDeserializer(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
objectMapper.registerModule(javaTimeModule);
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);
}
}
Combined with this configuration :
quarkus:
hibernate-orm:
mapping:
timezone:
default-storage: "NORMALIZE"
jdbc:
timezone: "Europe/Paris"
unsupported-properties:
- "hibernate.type.json_format_mapper": "pathToMyMapper"