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
Author
The following configuration is also required unless a public schema is used by the application (not likely)
javers:
# https://github.com/javers/javers/issues/934
sqlSchema: ${app.db.schema}
Author
InstantTruncatedTypeAdapter - Java Instant precision is 9 while PostgreSQL timestamp precision is 6 (rounded). As the result, JaVers reports created Instant as updated for an update after an insert (which JaVers records with nanos).
- repository.
save(entity) is called for a new entity- new
Instantvalue withnanosis generated by@org.springframework.data.annotation.CreatedDateannotation processor JaVersintercepts theentity.createdvalue passed to repository.save(entity)- actual value stored in the
PostgreSQLis set to roundedmicros
- new
- repository.
findById(id) is called to retrieve an entity- entity is returned with
entity.createdset to rounded micros (as retrieved fromPostgreSQL)
- entity is returned with
- an entity property is modified (e.g. name) and repository.
save(entity) is called for an updated entity JaVersintercepts theentity.createdvalue passed repository.save(entity)JaVersreports theentity.createdvalue changed because the insertsavecall containednanosand a subsequent updatesavecall contained roundedmicrosretrieved fromPostgreSQL
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Cross referencing javers/javers#1442