Package org.javers.core
Interface Javers
-
public interface Javers
Facade to JaVers instance.
Should be constructed byJaversBuilder
provided with your domain model configuration.
For example, to deeply compare two objects or two arbitrary complex graphs of objects, call:Javers javers = JaversBuilder.javers().build(); Diff diff = javers.compare(oldVersion, currentVersion);
-
-
Method Summary
All Methods Instance Methods Abstract Methods Deprecated Methods Modifier and Type Method Description Commit
commit(java.lang.String author, java.lang.Object currentVersion)
Persists a current state of a given domain object graph in JaVers repository.Commit
commit(java.lang.String author, java.lang.Object currentVersion, java.util.Map<java.lang.String,java.lang.String> commitProperties)
Variant ofcommit(String, Object)
with commitProperties.java.util.concurrent.CompletableFuture<Commit>
commitAsync(java.lang.String author, java.lang.Object currentVersion, java.util.concurrent.Executor executor)
java.util.concurrent.CompletableFuture<Commit>
commitAsync(java.lang.String author, java.lang.Object currentVersion, java.util.Map<java.lang.String,java.lang.String> commitProperties, java.util.concurrent.Executor executor)
Commit
commitShallowDelete(java.lang.String author, java.lang.Object deleted)
Marks given object as deleted.Commit
commitShallowDelete(java.lang.String author, java.lang.Object deleted, java.util.Map<java.lang.String,java.lang.String> commitProperties)
Variant ofcommitShallowDelete(String, Object)
with commitProperties.Commit
commitShallowDeleteById(java.lang.String author, GlobalIdDTO globalId)
The same likecommitShallowDelete(String,Object)
but deleted object is selected using globalIdCommit
commitShallowDeleteById(java.lang.String author, GlobalIdDTO globalId, java.util.Map<java.lang.String,java.lang.String> commitProperties)
Variant ofcommitShallowDeleteById(String, GlobalIdDTO)
with commitProperties.Diff
compare(java.lang.Object oldVersion, java.lang.Object currentVersion)
Deep compare<T> Diff
compareCollections(java.util.Collection<T> oldVersion, java.util.Collection<T> currentVersion, java.lang.Class<T> itemClass)
Deeply compares two top-level collections.Changes
findChanges(JqlQuery query)
Queries a JaversRepository for change history (diff sequence) of a given class, object or property.
Returns the list of Changes.
There are various types of changes.<T> java.util.List<Shadow<T>>
findShadows(JqlQuery query)
Queries JaversRepository for object Shadows.<T> java.util.stream.Stream<Shadow<T>>
findShadowsAndStream(JqlQuery query)
Streamed version offindShadows(JqlQuery)
.java.util.List<CdoSnapshot>
findSnapshots(JqlQuery query)
Queries JaversRepository for object Snapshots.java.util.Optional<CdoSnapshot>
getHistoricalSnapshot(java.lang.Object localId, java.lang.Class entity, java.time.LocalDateTime effectiveDate)
Historical snapshot of a given Entity instance.JsonConverter
getJsonConverter()
If you are serializing JaVers objects likeCommit
,Change
,Diff
orCdoSnapshot
to JSON, use this JsonConverter.java.util.Optional<CdoSnapshot>
getLatestSnapshot(java.lang.Object localId, java.lang.Class entity)
Latest snapshot of a given Entity instance.Property
getProperty(PropertyChange propertyChange)
ReturnsProperty
which underlies givenPropertyChange
<T extends JaversType>
TgetTypeMapping(java.lang.reflect.Type userType)
Use JaversTypes, if you want to:
- describe your class in the context of JaVers domain model mapping,
- use JaVers Reflection API to conveniently access your object properties (instead of awkward java.lang.reflect API).Diff
initial(java.lang.Object newDomainObject)
Deprecated.Usecompare(Object, Object)
passing null as the first parameter.<T> T
processChangeList(java.util.List<Change> changes, ChangeProcessor<T> changeProcessor)
Generic purpose method for processing a changes list.
-
-
-
Method Detail
-
commit
Commit commit(java.lang.String author, java.lang.Object currentVersion)
Persists a current state of a given domain object graph in JaVers repository.
JaVers applies commit() to given object and all objects navigable from it. You can capture a state of an arbitrary complex object graph with a single commit() call.- Parameters:
author
- current usercurrentVersion
- standalone object or handle to an object graph- See Also:
- http://javers.org/documentation/repository-examples
-
commit
Commit commit(java.lang.String author, java.lang.Object currentVersion, java.util.Map<java.lang.String,java.lang.String> commitProperties)
Variant ofcommit(String, Object)
with commitProperties.
You can pass arbitrary commit properties and use them in JQL to search for snapshots or changes.- Parameters:
commitProperties
- for example ["channel":"web", "locale":"pl-PL"]- See Also:
QueryBuilder.withCommitProperty(String, String)
-
commitAsync
java.util.concurrent.CompletableFuture<Commit> commitAsync(java.lang.String author, java.lang.Object currentVersion, java.util.concurrent.Executor executor)
Async version ofcommit(String, Object)
Important! Async Javers commits work seamless with MongoDB. If you are using SQL repository — take extra care about transaction management.- Parameters:
executor
- ExecutorService to be used to process commit() asynchronously.
-
commitAsync
java.util.concurrent.CompletableFuture<Commit> commitAsync(java.lang.String author, java.lang.Object currentVersion, java.util.Map<java.lang.String,java.lang.String> commitProperties, java.util.concurrent.Executor executor)
- Parameters:
executor
- ExecutorService to be used to process commit() asynchronously
-
commitShallowDelete
Commit commitShallowDelete(java.lang.String author, java.lang.Object deleted)
Marks given object as deleted.
Unlikecommit(String, Object)
, this method is shallow and affects only given object.
This method doesn't delete anything from JaVers repository. It just persists 'terminal snapshot' of a given object.- Parameters:
deleted
- object to be marked as deleted (Entity or Value Object)
-
commitShallowDelete
Commit commitShallowDelete(java.lang.String author, java.lang.Object deleted, java.util.Map<java.lang.String,java.lang.String> commitProperties)
Variant ofcommitShallowDelete(String, Object)
with commitProperties.
Seecommit(String, Object, Map)
for commitProperties description.
-
commitShallowDeleteById
Commit commitShallowDeleteById(java.lang.String author, GlobalIdDTO globalId)
The same likecommitShallowDelete(String,Object)
but deleted object is selected using globalId
-
commitShallowDeleteById
Commit commitShallowDeleteById(java.lang.String author, GlobalIdDTO globalId, java.util.Map<java.lang.String,java.lang.String> commitProperties)
Variant ofcommitShallowDeleteById(String, GlobalIdDTO)
with commitProperties.
Seecommit(String, Object, Map)
for commitProperties description.
-
compare
Diff compare(java.lang.Object oldVersion, java.lang.Object currentVersion)
Deep compare
JaVers core function, deeply compares two arbitrary complex object graphs.
To calculate a diff, just provide two versions of the same domain object.
The domain object can be a root of an Aggregate, tree root or any node in a domain object graph from where all other nodes are navigable.
BotholdVersion
andcurrentVersion
should be mapped toEntityType
orValueObjectType
, see Domain model mapping.Flat collection compare
You can also pass object collections here (List, Sets or Maps), but in this case, JaVers calculates flat collection diff only. Because it's impossible to determine type of raw collection items, JaVers maps them as Values and compares usingObject.equals(Object)
.
So if you need to deep compare, wrap collections in some Value Objects.Misc
compare()
function is used for ad-hoc objects comparing. In order to use data auditing feature, callcommit(String, Object)
.Diffs can be converted to JSON with
JsonConverter.toJson(Object)
or pretty-printed withDiff.prettyPrint()
- Parameters:
oldVersion
- Old version of a domain object, an instance ofEntityType
orValueObjectType
, nullablecurrentVersion
- Current version of a domain object, nullable- See Also:
- http://javers.org/documentation/diff-examples
-
compareCollections
<T> Diff compareCollections(java.util.Collection<T> oldVersion, java.util.Collection<T> currentVersion, java.lang.Class<T> itemClass)
Deeply compares two top-level collections.
Introduced due to the lack of possibility to statically determine type of collection items when two top-level collections are passed as references tocompare(Object, Object)
.
Usage example:List<Person> oldList = ... List<Person> newList = ... Diff diff = javers.compareCollections(oldList, newList, Person.class);
- See Also:
- Compare top-level collections example
-
initial
@Deprecated Diff initial(java.lang.Object newDomainObject)
Deprecated.Usecompare(Object, Object)
passing null as the first parameter.Initial diff is a kind of snapshot of a given object graph.
-
findShadows
<T> java.util.List<Shadow<T>> findShadows(JqlQuery query)
Queries JaversRepository for object Shadows.
Shadow is a historical version of a domain object restored from a snapshot.
For example, to get latest Shadows of "bob" Person, call:List
Since Shadows are instances of your domain classes, you can use them directly in your application:shadows = javers.findShadows( QueryBuilder.byInstanceId("bob", Person.class) .limit(5).build() ); assert shadows.get(0).get() instanceof Person.class;
Query scopes
By default, queries are run in the Shallow scope, which is the fastest one.
To eagerly load all referenced objects use one of the wider scopes, Commit-deep or Deep+ : We recommend the Deep+ scope as a good start.Query scopes example
To understand Shadow query scopes, you need to understand how JaVers commit works.
Remember that JaVers reuses existing snapshots and creates a fresh one only if a given object is changed.
The way how objects are committed affects shadow query results.For example, we have four Entities in the object graph, joined by references:
// E1 -> E2 -> E3 -> E4 def e4 = new Entity(id:4) def e3 = new Entity(id:3, ref:e4) def e2 = new Entity(id:2, ref:e3) def e1 = new Entity(id:1, ref:e2)
In the first scenario, our four entities are committed in three commits:
In Shallow scope, referenced entities are not loaded. But they all can be loaded using Deep+3 scope.given: javers.commit("author", e4) // commit 1.0 with e4 snapshot javers.commit("author", e3) // commit 2.0 with e3 snapshot javers.commit("author", e1) // commit 3.0 with snapshots of e1 and e2 when: 'shallow scope query' def shadows = javers.findShadows(QueryBuilder.byInstanceId(1, Entity) .build()) def shadowE1 = shadows.get(0).get() then: 'only e1 is loaded' shadowE1 instanceof Entity shadowE1.id == 1 shadowE1.ref == null when: 'commit-deep scope query' shadows = javers.findShadows(QueryBuilder.byInstanceId(1, Entity) .withScopeCommitDeep().build()) shadowE1 = shadows.get(0).get() then: 'only e1 and e2 are loaded, both was committed in commit 3.0' shadowE1.id == 1 shadowE1.ref.id == 2 shadowE1.ref.ref == null when: 'deep+1 scope query' shadows = javers.findShadows(QueryBuilder.byInstanceId(1, Entity) .withScopeDeepPlus(1).build()) shadowE1 = shadows.get(0).get() then: 'only e1 + e2 are loaded' shadowE1.id == 1 shadowE1.ref.id == 2 shadowE1.ref.ref == null when: 'deep+3 scope query' shadows = javers.findShadows(QueryBuilder.byInstanceId(1, Entity) .withScopeDeepPlus(3).build()) shadowE1 = shadows.get(0).get() then: 'all object are loaded' shadowE1.id == 1 shadowE1.ref.id == 2 shadowE1.ref.ref.id == 3 shadowE1.ref.ref.ref.id == 4
In the second scenario, our four entities are committed in the single commit:
Shallow scope works in the same way. Commit-deep scope is enough to load the full graph.given: javers.commit("author", e1) //commit 1.0 with snapshots of e1, e2, e3 and e4 when: 'commit-deep scope query' shadows = javers.findShadows(QueryBuilder.byInstanceId(1, Entity) .withScopeCommitDeep().build()) shadowE1 = shadows.get(0).get() then: 'all object are loaded' shadowE1.id == 1 shadowE1.ref.id == 2 shadowE1.ref.ref.id == 3 shadowE1.ref.ref.ref.id == 4
Performance & Profiling
Each Shadow query executes one or more Snapshot queries (depending on the scope) and complexity of an object graph. If you are having performance issues, check with debug mode how your Shadow query is executed and then, try to reduce the scope.<logger name="org.javers.JQL" level="DEBUG"/>
Example debug for a Shadow query execution:DEBUG org.javers.JQL - SHALLOW query: 1 snapshots loaded (entities: 1, valueObjects: 0) DEBUG org.javers.JQL - DEEP_PLUS query for '...SnapshotEntity/2' at commitId 3.0, 1 snapshot(s) loaded, gaps filled so far: 1 DEBUG org.javers.JQL - warning: object '...SnapshotEntity/3' is outside of the DEEP_PLUS+1 scope, references to this object will be nulled. Increase maxGapsToFill and fill all gaps in your object graph. DEBUG org.javers.JQL - queryForShadows executed: JqlQuery { IdFilter{ globalId: ...SnapshotEntity/1 } QueryParams{ aggregate: true, limit: 100 } ShadowScopeDefinition{ shadowScope: DEEP_PLUS, maxGapsToFill: 1 } Stats{ executed in millis: 12 DB queries: 2 all snapshots: 2 SHALLOW snapshots: 1 DEEP_PLUS snapshots: 1 gaps filled: 1 gaps left!: 1 } }
Execution stats are also available inJqlQuery.stats()
.- Type Parameters:
T
- type of a domain object- Returns:
- A list of Shadows, ordered in reverse chronological order. Empty if nothing found.
Remember that
QueryBuilder.limit(int)
limits the number of Snapshots loaded in the base query and not the number of returned Shadows (one Shadow can be reconstructed from many Snapshots). The only correct way for paging Shadows isfindShadowsAndStream(JqlQuery)
withStream.skip(long)
andStream.limit(long)
. - Since:
- 3.2
- See Also:
ShadowScope
, http://javers.org/documentation/jql-examples
-
findShadowsAndStream
<T> java.util.stream.Stream<Shadow<T>> findShadowsAndStream(JqlQuery query)
Streamed version offindShadows(JqlQuery)
.
UsingStream.skip(long)
andStream.limit(long)
is the only correct way for paging Shadows.- Type Parameters:
T
- type of a domain object- Returns:
- A stream of Shadows, ordered in reverse chronological order. Terminated stream if nothing found.
- Since:
- 3.10
- See Also:
findShadows(JqlQuery)
-
findChanges
Changes findChanges(JqlQuery query)
Queries a JaversRepository for change history (diff sequence) of a given class, object or property.
Returns the list of Changes.
There are various types of changes. SeeChange
class hierarchy.
Changes
can be easily traversed usingChanges.groupByCommit()
andChanges.groupByObject()
.
Querying for Entity changes by instance Id
For example, to get change history of last 5 versions of "bob" Person, call:javers.findChanges( QueryBuilder.byInstanceId("bob", Person.class).limit(5).build() );
Last "salary" changes of "bob" Person:javers.findChanges( QueryBuilder.byInstanceId("bob", Person.class).withChangedProperty("salary").build() );
Querying for ValueObject changes
Last changes on Address ValueObject owned by "bob" Person:javers.findChanges( QueryBuilder.byValueObjectId("bob", Person.class, "address").build() );
Last changes on Address ValueObject owned by any Person:javers.findChanges( QueryBuilder.byValueObject(Person.class, "address").build() );
Last changes on nested ValueObject (when Address is a ValueObject nested in PersonDetails ValueObject):javers.findChanges( QueryBuilder.byValueObject(Person.class, "personDetails/address").build() );
Querying for any object changes by its class
Last changes on any object of MyClass.class:javers.findChanges( QueryBuilder.byClass(MyClass.class).build() );
Last "myProperty" changes on any object of MyClass.class:javers.findChanges( QueryBuilder.byClass(Person.class).withChangedProperty("myProperty").build() );
- Returns:
- A list of Changes ordered in reverse chronological order. Empty if nothing found.
- See Also:
- http://javers.org/documentation/jql-examples
-
findSnapshots
java.util.List<CdoSnapshot> findSnapshots(JqlQuery query)
Queries JaversRepository for object Snapshots.
Snapshot is a historical state of a domain object captured as the property->value Map.
For example, to get latest Snapshots of "bob" Person, call:javers.findSnapshots( QueryBuilder.byInstanceId("bob", Person.class).limit(5).build() );
For more query examples, seefindChanges(JqlQuery)
method.
Use the same JqlQuery to get changes, snapshots and shadows views.- Returns:
- A list ordered in reverse chronological order. Empty if nothing found.
- See Also:
- http://javers.org/documentation/jql-examples
-
getLatestSnapshot
java.util.Optional<CdoSnapshot> getLatestSnapshot(java.lang.Object localId, java.lang.Class entity)
Latest snapshot of a given Entity instance.
For example, to get the current state ofBob
, call:javers.getLatestSnapshot("bob", Person.class);
Returns Optional#EMPTY if an instance is not versioned.
-
getHistoricalSnapshot
java.util.Optional<CdoSnapshot> getHistoricalSnapshot(java.lang.Object localId, java.lang.Class entity, java.time.LocalDateTime effectiveDate)
Historical snapshot of a given Entity instance.
For example, to get the historical state ofBob
at 2017-01-01, call:javers.getHistoricalSnapshot("bob", Person.class, LocalDateTime.of(2017,01,01));
Returns Optional#EMPTY if an instance is not versioned.- Since:
- 3.4
-
getJsonConverter
JsonConverter getJsonConverter()
If you are serializing JaVers objects likeCommit
,Change
,Diff
orCdoSnapshot
to JSON, use this JsonConverter.
For example:javers.getJsonConverter().toJson(changes);
-
processChangeList
<T> T processChangeList(java.util.List<Change> changes, ChangeProcessor<T> changeProcessor)
Generic purpose method for processing a changes list. After iterating over given list, returns data computed byChangeProcessor.result()
.
It's more convenient than iterating over changes on your own. ChangeProcessor frees you from if + inctanceof boilerplate.
Additional features:
- when several changes in a row refers to the same Commit,ChangeProcessor.onCommit(CommitMetadata)
is called only for first occurrence
- similarly, when several changes in a row affects the same object,ChangeProcessor.onAffectedObject(GlobalId)
is called only for first occurrence
For example, to get pretty change log, call:List<Change> changes = javers.calculateDiffs(...); String changeLog = javers.processChangeList(changes, new SimpleTextChangeLog()); System.out.println( changeLog );
- See Also:
SimpleTextChangeLog
-
getTypeMapping
<T extends JaversType> T getTypeMapping(java.lang.reflect.Type userType)
Use JaversTypes, if you want to:
- describe your class in the context of JaVers domain model mapping,
- use JaVers Reflection API to conveniently access your object properties (instead of awkward java.lang.reflect API).
Class describe example. You can pretty-print JaversType of your class and check if mapping is correct.class Person { @Id int id; @Transient String notImportantField; String name; }
CallingSystem.out.println( javers.getTypeMapping(Person.class).prettyPrint() );
prints:EntityType{ baseType: org.javers.core.examples.Person managedProperties: Field int id; //declared in: Person Field String name; //declared in: Person idProperty: login }
Property access example. You can list object property values usingProperty
abstraction.Javers javers = JaversBuilder.javers().build(); ManagedType jType = javers.getTypeMapping(Person.class); Person person = new Person("bob", "Uncle Bob"); System.out.println("Bob's properties:"); for (Property p : jType.getPropertyNames()){ Object value = p.get(person); System.out.println( "property:" + p.getName() + ", value:" + value ); }
prints:Bob's properties: property:login, value:bob property:name, value:Uncle Bob
-
getProperty
Property getProperty(PropertyChange propertyChange)
ReturnsProperty
which underlies givenPropertyChange
- Since:
- 1.4.1
-
-