Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EntityType and ValueObjectType spawning/interring not working properly with Serializable interface (or other parent interfaces) #601

Closed
philippeboyd opened this issue Nov 17, 2017 · 4 comments
Labels

Comments

@philippeboyd
Copy link

So I've encountered a weird bug (or feature?) in Javers. The method ReflectionUtil.calculateHierarchyDistance() calculates the distance to be used in TypeMapperState.findNearestAncestor(). However, in the latter method there's this check if(distances.get(0).isMax()) that will allow a null value to be returned by the method to be then used as an Optional.ofNullable(prototype) in the TypeMapperState.infer which will cause the prototype to NOT be present in the TypeFactory.infer() method which will then allow the javatype to be passed to inferFromAnnotations and eventually end up as an EntityType and not a ValueObjectType.

Here is my class hierarchy:

AbstractPermission -> Permission
AbstractPermission -> Role

Here are the logs :

With Serializable interface on parent class

o.j.core.metamodel.type.TypeFactory      : javersType of interface java.io.Serializable inferred as ValueObjectType

o.j.core.metamodel.type.TypeFactory      : javersType of class org.javers.mongo.javersmongoproblem.domain.Permission inferred as EntityType
o.j.core.metamodel.type.TypeFactory      : javersType of class java.lang.String inferred as ValueType, it's used as id-property type

o.j.core.metamodel.type.TypeFactory      : javersType of java.util.Set<java.lang.String> spawned as SetType from prototype SetType{baseType:interface java.util.Set}

o.j.core.metamodel.type.TypeFactory      : javersType of class org.javers.mongo.javersmongoproblem.domain.Role spawned as ValueObjectType from prototype ValueObjectType{baseType:interface java.io.Serializable}

o.j.core.metamodel.type.TypeFactory      : javersType of java.util.Set<org.javers.mongo.javersmongoproblem.domain.Permission> spawned as SetType from prototype SetType{baseType:java.util.Set<java.lang.String>}

Without Serializable interface on parent class

o.j.core.metamodel.type.TypeFactory      : javersType of interface java.io.Serializable inferred as ValueObjectType

o.j.core.metamodel.type.TypeFactory      : javersType of class org.javers.mongo.javersmongoproblem.domain.Permission inferred as EntityType
o.j.core.metamodel.type.TypeFactory      : javersType of class java.lang.String inferred as ValueType, it's used as id-property type

o.j.core.metamodel.type.TypeFactory      : javersType of java.util.Set<java.lang.String> spawned as SetType from prototype SetType{baseType:interface java.util.Set}

o.j.core.metamodel.type.TypeFactory      : javersType of class org.javers.mongo.javersmongoproblem.domain.Role inferred as EntityType
o.j.core.metamodel.type.TypeFactory      : javersType of class java.lang.String inferred as ValueType, it's used as id-property type

o.j.core.metamodel.type.TypeFactory      : javersType of java.util.Set<org.javers.mongo.javersmongoproblem.domain.Permission> spawned as SetType from prototype SetType{baseType:java.util.Set<java.lang.String>}

When AbstractPermission doesn't implement Serializable both object are known as EntityType during.

When AbstractPermission implements Serializable, Role object will get spawned as ValueObjectType from prototype ValueObjectType{baseType:interface java.io.Serializable} and will get the following error when getting changes

QueryBuilder jqlQuery2 = QueryBuilder.byInstanceId(permissionReadUser.getId(), Permission.class);
List<Change> changes2 = javers.findChanges(jqlQuery2.build());

Will throw a :

Caused by: org.javers.common.exception.JaversException: MANAGED_CLASS_MAPPING_ERROR: given javaClass 'class org.javers.mongo.javersmongoproblem.domain.Permission' is mapped to ValueObjectType, expected EntityType

It's a really complex problem and I'm pretty sure ReflectionUtil.calculateHierarchyDistance() isn't calculating something right...

You can test my case with my repository https://github.com/philippeboyd/javers-issue-managed-type

@bartoszwalacik
Copy link
Member

bartoszwalacik commented Nov 17, 2017

Interesting case. Thanks for providing the runnable test. I will take a look at this.

@philippeboyd
Copy link
Author

I just found a workaround. Instead of letting Javers creating those Managed Type, I copied the Javers bean from the JaversMongoAutoConfiguration class and explicitly registered my Classes like so :

@DependsOn("mongoTemplate")
@Bean
public Javers javers(MongoTemplate mongoTemplate, JaversProperties javersProperties) {
	return JaversBuilder.javers()
		.withListCompareAlgorithm(ListCompareAlgorithm.valueOf(javersProperties.getAlgorithm().toUpperCase()))
		.withMappingStyle(MappingStyle.valueOf(javersProperties.getMappingStyle().toUpperCase()))
		.withNewObjectsSnapshot(javersProperties.isNewObjectSnapshot())
		.withPrettyPrint(javersProperties.isPrettyPrint())
		.withTypeSafeValues(javersProperties.isTypeSafeValues())
		.registerJaversRepository(new MongoRepository(mongoTemplate.getDb()))
		.withPackagesToScan(javersProperties.getPackagesToScan())
		.registerEntities(Role.class, Permission.class) // <-- added line
		.build();
}

I'll leave this open for you @bartoszwalacik if you want to dig a little deeper.

@bartoszwalacik
Copy link
Member

It's a good workaround, you can register your types explicitly but type inferring should also work. I will check why it doesn't.

bartoszwalacik added a commit that referenced this issue Nov 20, 2017
bartoszwalacik added a commit that referenced this issue Nov 23, 2017
@bartoszwalacik
Copy link
Member

fix released in 3.7.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants