Created
June 24, 2025 19:02
-
-
Save pavelfomin/380fec9c966428dfdd8ef4049b8386d2 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import com.fasterxml.jackson.annotation.JsonProperty; | |
| import com.fasterxml.jackson.annotation.JsonTypeInfo; | |
| import org.javers.core.ChangesByCommit; | |
| import org.javers.core.diff.Change; | |
| import org.javers.core.json.BasicStringTypeAdapter; | |
| import org.javers.core.json.typeadapter.util.UtilTypeCoreAdapters; | |
| import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; | |
| import org.springframework.context.annotation.Bean; | |
| import org.springframework.context.annotation.Configuration; | |
| import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; | |
| import java.time.Instant; | |
| import java.time.temporal.ChronoField; | |
| import java.util.List; | |
| @Configuration | |
| public class JaVersConfiguration { | |
| @Bean | |
| InstantTruncatedTypeAdapter instantTruncatedTypeAdapter() { | |
| return new InstantTruncatedTypeAdapter(); | |
| } | |
| /** | |
| * The Jackson2ObjectMapperBuilder context can be customized by one or more Jackson2ObjectMapperBuilderCustomizer beans. | |
| * Such customizer beans can be ordered (the Boot customizer has an order of 0), letting additional customization | |
| * be applied both before and after the Boot customization. | |
| */ | |
| @Bean | |
| Jackson2ObjectMapperBuilderCustomizer jacksonJaVersCustomizer() { | |
| return (Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) -> | |
| jacksonObjectMapperBuilder | |
| .mixIn(ChangesByCommit.class, JaVersChangesMixIn.class) | |
| .mixIn(Change.class, ClassNameTypeMixIn.class); | |
| } | |
| /** | |
| * Truncated to microseconds {@link Instant} type adapter. | |
| * Instant precision is 9 while PostgreSQL timestamp precision is 6 (rounded). | |
| * See {@link org.javers.java8support.InstantTypeAdapter}. | |
| */ | |
| static class InstantTruncatedTypeAdapter extends BasicStringTypeAdapter<Instant> { | |
| @Override | |
| public String serialize(Instant sourceValue) { | |
| return UtilTypeCoreAdapters.serialize(sourceValue | |
| .with(ChronoField.MICRO_OF_SECOND, | |
| Math.round((double) sourceValue.get(ChronoField.NANO_OF_SECOND) / 1000))); | |
| } | |
| @Override | |
| public Instant deserialize(String serializedValue) { | |
| return UtilTypeCoreAdapters.deserializeToInstant(serializedValue); | |
| } | |
| @Override | |
| public Class<?> getValueType() { | |
| return Instant.class; | |
| } | |
| } | |
| /** | |
| * Makes `changes` returned by non-standard `get()` method serializable by Jackson. | |
| */ | |
| abstract static class JaVersChangesMixIn { | |
| @JsonProperty("changes") | |
| abstract List<Change> get(); | |
| } | |
| /** | |
| * Instructs Jackson to serialize class name as `type` attribute. | |
| * Useful for JaVers Change subclasses, especially org.javers.core.diff.changetype.NewObject. | |
| */ | |
| @JsonTypeInfo(use = JsonTypeInfo.Id.SIMPLE_NAME, property = "type") | |
| abstract static class ClassNameTypeMixIn { | |
| } | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
InstantTruncatedTypeAdapter - Java
Instantprecision is9whilePostgreSQLtimestamp precision is6(rounded). As the result, JaVers reportscreatedInstant as updated for an update after an insert (which JaVers records with nanos).save(entity) is called for a new entityInstantvalue withnanosis generated by@org.springframework.data.annotation.CreatedDateannotation processorJaVersintercepts theentity.createdvalue passed to repository.save(entity)PostgreSQLis set to roundedmicrosfindById(id) is called to retrieve an entityentity.createdset to rounded micros (as retrieved fromPostgreSQL)save(entity) is called for an updated entityJaVersintercepts theentity.createdvalue passed repository.save(entity)JaVersreports theentity.createdvalue changed because the insertsavecall containednanosand a subsequent updatesavecall contained roundedmicrosretrieved fromPostgreSQL