Reputation: 21
I am evaluating javers to use it for auditing entities. I have an Entity with nested collection of ValueObjects.I expect each attribute change on the valueobject to generate a snapshot of the Entity.Snapshot is created only when a valueobject is added to the collection.In my case i added two valueobjects to the collection which created two snapshots of the entity. On third occasion i just changed an attribute on value object, and javers didn't recognize that as a change on the entity but created a snapshot for inner value objects.
My question is whether my assumption is valid or what is the best way to track the changes to the value objects in a collection
Below is the code from a simple test i have created using spring boot.
I am using javers version 3.2.0
My entity is as below
package com.example.javersdemo;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import lombok.Data;
import org.javers.core.metamodel.annotation.TypeName;
import javax.persistence.CollectionTable;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import java.util.List;
@Data
@Entity
@TypeName("User")
class User{
@Id
private String name ;
@JsonUnwrapped
@ElementCollection(targetClass = Hobby.class)
@CollectionTable(name = "USER_HOBBIES")
@JoinColumn(name = "NAME")
private List<Hobby> hobbies;
private User(){
}
public User(String name, List<Hobby> hobbies) {
this.name = name;
this.hobbies = hobbies;
}
}
Value object is as below
package com.example.javersdemo;
import lombok.Data;
import javax.persistence.Embeddable;
@Data
@Embeddable
public class Hobby {
private String hobby;
private boolean active;
private Hobby() {
}
public Hobby(String hobby, boolean active) {
this.hobby = hobby;
this.active = active;
}
}
My spring data repository is as below
package com.example.javersdemo;
import org.javers.spring.annotation.JaversSpringDataAuditable;
import org.springframework.data.repository.CrudRepository;
@JaversSpringDataAuditable
interface TestUserRepository extends CrudRepository<User,String> {
}
Below is a spock integration test i have created to verify the changes to the object attributes inside a collection creates a new snapshot.
package com.example.javersdemo
import org.javers.core.Javers
import org.javers.repository.jql.QueryBuilder
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import spock.lang.Specification
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class JaversInnerValueObjectsTest extends Specification {
@Autowired
TestUserRepository userRepository
@Autowired
Javers javers
def 'should create 6 snapshots'() {
given:
def hobbies = [new Hobby('Reading books', true)]
def user = new User('John', hobbies)
when:
userRepository.save(user)
hobbies = [new Hobby('Reading books', true), new Hobby('Watching Soccer', true)]
user.hobbies = hobbies
userRepository.save(user)
hobbies = [new Hobby('Reading books', true), new Hobby('Watching Soccer', false)]
user.hobbies = hobbies
userRepository.save(user)
then:
QueryBuilder jqlQuery = QueryBuilder.byInstanceId('John', User)
def snapshots = javers.findSnapshots(jqlQuery.withChildValueObjects().build())
snapshots.size() == 6
}
}
and the test fails with below error
Condition not satisfied:
snapshots.size() == 6 | | | | 5 false [Snapshot{commit:3.0, id:User/John#hobbies/1, version:2, (hobby:Watching Soccer)}, Snapshot{commit:2.0, id:User/John#hobbies/1, version:1, (active:true, hobby:Watching Soccer)}, Snapshot{commit:2.0, id:User/John, version:2, (hobbies:[User/John#hobbies/0, User/John#hobbies/1], name:John)}, Snapshot{commit:1.0, id:User/John#hobbies/0, version:1, (active:true, hobby:Reading books)}, Snapshot{commit:1.0, id:User/John, version:1, (hobbies:[User/John#hobbies/0], name:John)}]
Expected :6
Actual :5
Upvotes: 2
Views: 2036
Reputation: 3496
In the log there are JaVers commit statistis:
13:27:44.315 [main] INFO org.javers.core.Javers - Commit(id:1.0, snapshots:2, author:author, changes - NewObject:2), done in 71 millis (diff:71, persist:0)
13:27:44.333 [main] INFO org.javers.core.Javers - Commit(id:2.0, snapshots:2, author:author, changes - ListChange:1 NewObject:1), done in 17 millis (diff:17, persist:0)
13:27:44.336 [main] INFO org.javers.core.Javers - Commit(id:3.0, snapshots:1, author:author, changes - ValueChange:1), done in 3 millis (diff:3, persist:0)
In the firts commit, 2 snapshots are created because we have 2 new objects here.
In the second commit another 2 snapshots are created:
hobbies
is changed - new item added to the list so ListChangeIn the third commit only 1 snapshot is created, because user's field hobbies
isn't changed. JaVers treats collections of Value Objects and collections of Entities as collections of references (GlobalId's). In this case, references are not changed. The collection's state is captured as follows:
["User/John#hobbies/0",
"User/John#hobbies/1"]
The only change in the third commit is the Hobby's field change (true->false) so ValueChange in ValueObject with GlobalId User/John#hobbies/1
In other words, when you change Value Objects owned by some Entity, and there is no change in Entity state, Entity snapshot will not be created, because it would be exactly the same as the previous one.
Upvotes: 0