Reputation: 11447
I am currently using spring data mongodb and the Configuration file extends AbstractMongoConfiguration:
@Configuration
@EnableMongoRepositories(basePackages = "com.mycompany")
@EnableMongoAuditing
public class MongoConfig extends AbstractMongoConfiguration
{
I override the getMappingBasePackage()
method to set the package to scan like this:
@Override
protected String getMappingBasePackage()
{
return "com.mycompany";
}
I have been debugging through the code and noticed some interesting things:
java.lang.InstantiationError
. Both cases occur when I am trying to read in document from mongo that has a reference to an abstract class (ParentClass). It is trying to instantiate the abstract class instead of finding the @TypeAlias
annotation which I have added to the child classes.This is what my ParentClass looks like:
@Document
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.EXISTING_PROPERTY, visible=true, property="type")
@JsonSubTypes({
@Type(value=Child1.class, name="JSON_TYPE_CHILD1"),
@Type(value=Child2.class, name="JSON_TYPE_CHILD2"),
@Type(value=Child3.class, name="JSON_TYPE_CHILD3")
})
public abstract class ParentClass
{
...
My child classes look like this:
@Document
@JsonTypeName("JSON_TYPE_CHILD1")
@TypeAlias("ALIAS_TYPE_CHILD1")
public class Child1 extends ParentClass
{
...
This is what the json looks like (simplified) that I am trying to read in:
{
"_id" : ObjectId("5c86d31388f13344f4098c64"),
"listOfWrapperClass" : [
{
"parentClass" : {
"type" : "JSON_TYPE_CHILD1",
"prop1" : 50.0,
"prop2" : 50.0,
"_class" : "ALIAS_TYPE_CHILD1"
},
"isReportOutOfDate" : false,
}
],
"_class" : "com.mycompany.domain.job.Job"
}
When I debug through spring data the problem occurs in DefaultTypeMapper:
private TypeInformation<?> getFromCacheOrCreate(Alias alias) {
Optional<TypeInformation<?>> typeInformation = typeCache.get(alias);
if (typeInformation == null) {
typeInformation = typeCache.computeIfAbsent(alias, getAlias);
}
return typeInformation.orElse(null);
}
It load the wrapper class fine, but when it gets to child class the alias is set to "ALIAS_TYPE_CHILD1" as it should but the following values are in the typeCache:
{
NONE=Optional.empty,
ALIAS_TYPE_CHILD1=Optional.empty,
com.mycompany.domain.job.Job=Optional[com.mycompany.domain.job.Job]
}
Because the key "ALIAS_TYPE_CHILD1" has an Optional.empty as a value the code doesn't get the correct target type to load and it therefore uses the rawType which is the ParentClass. Which blows up because it can't instantiate an abstract class. Here is the stacktrace:
Caused by: java.lang.InstantiationError: com.mycompany.domain.job.base.ParentClass
at com.mycompany.domain.job.base.ParentClass_Instantiator_q3kytg.newInstance(Unknown Source)
at org.springframework.data.convert.ClassGeneratingEntityInstantiator$EntityInstantiatorAdapter.createInstance(ClassGeneratingEntityInstantiator.java:226)
at org.springframework.data.convert.ClassGeneratingEntityInstantiator.createInstance(ClassGeneratingEntityInstantiator.java:84)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:272)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:245)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readValue(MappingMongoConverter.java:1491)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$MongoDbPropertyValueProvider.getPropertyValue(MappingMongoConverter.java:1389)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readProperties(MappingMongoConverter.java:378)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.populateProperties(MappingMongoConverter.java:295)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:275)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:245)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readCollectionOrArray(MappingMongoConverter.java:1038)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readValue(MappingMongoConverter.java:1489)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$MongoDbPropertyValueProvider.getPropertyValue(MappingMongoConverter.java:1389)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readProperties(MappingMongoConverter.java:378)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.populateProperties(MappingMongoConverter.java:295)
at ...
The weird thing is if I insert a new document that has @TypeAlias("ALIAS_TYPE_CHILD1")
first, the typeCache
mentioned above is populated correctly like so:
{
NONE=Optional.empty,
ALIAS_TYPE_CHILD1=Optional[com.mycompany.domain.job.base.Child1],
com.mycompany.domain.job.Job=Optional[com.mycompany.domain.job.Job]
}
When I do findOne right after the insert, I can read in the document without error because it uses Child1 to instantiate the pojo instead of the ParentClass. If I try to read first then it doesn't matter if I insert after that or not because the typeCace
gets the wrong value in there and it uses that until you restart the server.
My guess is there was a change in configuration or in a default setting. I was able to work through all the other upgrade issues, but this one has me baffled. I would be shocked if there is an actual issue in spring data because I am sure somebody would have run into this issue by now because I can't be the only one trying to use @TypeAlias
with spring-data-mongodb. Not to mention this all works great with the previous version of spring boot that I used (1.4.5 which uses spring-data-mongodb 1.9.8.RELEASE).
Any thoughts or advice on what to try next are welcome. I am simply at a loss of what to do next.
Upvotes: 4
Views: 3248
Reputation: 11447
The problem was the fact that the typeCache wasn't getting populated in the first place on server startup. This is because protected String getMappingBasePackage()
is now deprecated. You should use protected Collection<String> getMappingBasePackages()
instead and then everything works great.
Overriding this method solves the issue:
@Override
protected Collection<String> getMappingBasePackages()
{
return Arrays.asList("com.mycompany");
}
Upvotes: 7