spring-data-mongodb
spring-data-mongodb copied to clipboard
BeanInstantiationException: Failed to instantiate []: Specified class is an interface [DATAMONGO-2391]
Ilya Zinkevich opened DATAMONGO-2391 and commented
Hi,
I get error with package org.springframework.boot:spring-boot-starter-data-mongodb:2.1.5.RELEASE
Description:
I have interface Call
and class TokBoxCall
(implements Call
) in the code.
There is default typeKey in the records in Mongo DB:
"_class":"com.xxxxx.server.data.model.call.TokBoxCall".
And I didn't overwrite it in Spring Mongo Config.
However, when I perform reading operations, e.g. callRepository.findById(id)
, I get Exception:
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.xxxxx.server.data.model.Call]: Specified class is an interface
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:119)
at org.springframework.data.convert.ReflectionEntityInstantiator.createInstance(ReflectionEntityInstantiator.java:64)}}
Affects: 2.1.11 (Lovelace SR11)
1 votes, 4 watchers
Christoph Strobl commented
Thanks for reporting the issue.
I tried to reproduce the issue but everything seems to work as designed. Maybe I'm missing something here. Do you have a failing example at hand?
Ilya Zinkevich commented
Due to NDA, I cannot provide a real code.
The difference is that
gradle build && java -jar
allows to reproduce the issue, whereas
gradle bootRun
works fine
Ilya Zinkevich commented
Error occurs when I call this findOne:
public interface CallRepository extends MongoRepository<Call, String>, CallRepositoryCustom {
default Call findOne(String callId) {
return findById(callId).orElse(null);
}
}
Ilya Zinkevich commented
Correct logs look:
2019-10-24 19:44:15,472 [ qtp192486017-26 ] DEBUG [ o.s.d.m.c.MongoTemplate:2430 ] find using query: { "$or" : [{ "$or" : [{ "_id" : { "$oid" : "593aa90bbe55b4134533f7c7" }, "allRoles" : { "$in" : ["XXXXX"] } }, { "_id" : { "$oid" : "593aa90bbe55b4134533f7c7" }, "allRoles" : { "$in" : ["XXXXX"] } }] }, { "_id" : { "$oid" : "593aa90bbe55b4134533f7c7" }, "allRoles" : { "$in" : ["XXXXXX"] } }] } fields: Document{{}} for class: class com.xxxxxxxx.server.data.model.User in collection: users
2019-10-24 19:44:15,497 [ mmonPool-worker-13 ] DEBUG [ o.s.d.m.c.MongoTemplate:2430 ] find using query: { "$or" : [{ "$or" : [{ "_id" : { "$oid" : "593aa90bbe55b4134533f7c7" }, "allRoles" : { "$in" : ["XXXXX"] } }, { "_id" : { "$oid" : "593aa90bbe55b4134533f7c7" }, "allRoles" : { "$in" : ["XXXXX"] } }] }, { "_id" : { "$oid" : "593aa90bbe55b4134533f7c7" }, "allRoles" : { "$in" : ["XXXXXX"] } }] } fields: Document{{}} for class: class com.xxxxxxxx.server.data.model.User in collection: users
2019-10-24 19:44:15,497 [ mmonPool-worker-15 ] DEBUG [ o.s.d.m.c.MongoTemplate:2430 ] find using query: { "$or" : [{ "$or" : [{ "_id" : { "$oid" : "593aa90bbe55b4134533f7c7" }, "allRoles" : { "$in" : ["XXXXX"] } }, { "_id" : { "$oid" : "593aa90bbe55b4134533f7c7" }, "allRoles" : { "$in" : ["XXXXX"] } }] }, { "_id" : { "$oid" : "593aa90bbe55b4134533f7c7" }, "allRoles" : { "$in" : ["XXXXXX"] } }] } fields: Document{{}} for class: class com.xxxxxxxx.server.data.model.User in collection: users
2019-10-24 19:44:15,497 [ qtp192486017-26 ] DEBUG [ o.s.d.m.c.MongoTemplate:2430 ] find using query: { "$or" : [{ "$or" : [{ "_id" : { "$oid" : "593aa90bbe55b4134533f7c7" }, "allRoles" : { "$in" : ["XXXXX"] } }, { "_id" : { "$oid" : "593aa90bbe55b4134533f7c7" }, "allRoles" : { "$in" : ["XXXXX"] } }] }, { "_id" : { "$oid" : "593aa90bbe55b4134533f7c7" }, "allRoles" : { "$in" : ["XXXXXX"] } }] } fields: Document{{}} for class: class com.xxxxxxxx.server.data.model.User in collection: users
2019-10-24 19:44:15,501 [ mmonPool-worker-13 ] DEBUG [ o.s.d.m.c.MongoTemplate:2356 ] findOne using query: { "id" : "5d91df3e46e0fb0001ff4d0a" } fields: Document{{}} for class: interface com.xxxxxxxx.server.data.model.Call in collection: calls
2019-10-24 19:44:15,503 [ mmonPool-worker-13 ] DEBUG [ o.s.d.m.c.MongoTemplate:2811 ] findOne using query: { "_id" : { "$oid" : "5d91df3e46e0fb0001ff4d0a" } } fields: { } in db.collection: xxxx-debug.calls
2019-10-24 19:44:15,506 [ qtp192486017-26 ] INFO [ o.m.driver.connection: 71 ] Opened connection [connectionId{localValue:12, serverValue:1148}] to 127.0.0.1:47017
2019-10-24 19:44:15,512 [ mmonPool-worker-15 ] INFO [ o.m.driver.connection: 71 ] Opened connection [connectionId{localValue:13, serverValue:1149}] to 127.0.0.1:47017
2019-10-24 19:44:15,516 [ mmonPool-worker-15 ] DEBUG [ o.s.d.m.c.MongoTemplate:2356 ] findOne using query: { "id" : "5d91df3e46e0fb0001ff4d0a" } fields: Document{{}} for class: interface com.xxxxxxxx.server.data.model.Call in collection: calls
2019-10-24 19:44:15,516 [ qtp192486017-26 ] DEBUG [ o.s.d.m.c.MongoTemplate:2356 ] findOne using query: { "id" : "5d91df3e46e0fb0001ff4d0a" } fields: Document{{}} for class: interface com.xxxxxxxx.server.data.model.Call in collection: calls
2019-10-24 19:44:15,517 [ mmonPool-worker-15 ] DEBUG [ o.s.d.m.c.MongoTemplate:2811 ] findOne using query: { "_id" : { "$oid" : "5d91df3e46e0fb0001ff4d0a" } } fields: { } in db.collection: xxxx-debug.calls
2019-10-24 19:44:15,518 [ qtp192486017-26 ] DEBUG [ o.s.d.m.c.MongoTemplate:2811 ] findOne using query: { "_id" : { "$oid" : "5d91df3e46e0fb0001ff4d0a" } } fields: { } in db.collection: xxxx-debug.calls
2019-10-24 19:44:15,538 [ qtp192486017-26 ] INFO [ o.m.driver.connection: 71 ] Opened connection [connectionId{localValue:14, serverValue:1150}] to 127.0.0.1:47017
2019-10-24 19:44:15,652 [ qtp192486017-26 ] DEBUG [ o.s.d.m.c.MongoTemplate:2356 ] findOne using query: { "id" : "5d91df3e46e0fb0001ff4d0a" } fields: Document{{}} for class: interface com.xxxxxxxx.server.data.model.Call in collection: calls
2019-10-24 19:44:15,653 [ qtp192486017-26 ] DEBUG [ o.s.d.m.c.MongoTemplate:2811 ] findOne using query: { "_id" : { "$oid" : "5d91df3e46e0fb0001ff4d0a" } } fields: { } in db.collection: xxxx-debug.calls
Ilya Zinkevich commented
Incorrect case logs:
2019-10-24 19:46:31,500 [ qtp1709700394-23 ] DEBUG [ o.s.d.m.c.MongoTemplate:2430 ] find using query: { "$or" : [{ "$or" : [{ "_id" : { "$oid" : "593aa90bbe55b4134533f7c7" }, "allRoles" : { "$in" : ["XXXXX"] } }, { "_id" : { "$oid" : "593aa90bbe55b4134533f7c7" }, "allRoles" : { "$in" : ["XXXXX"] } }] }, { "_id" : { "$oid" : "593aa90bbe55b4134533f7c7" }, "allRoles" : { "$in" : ["XXXXXX"] } }] } fields: Document{{}} for class: class com.xxxxxxxx.server.data.model.User in collection: users
2019-10-24 19:46:31,527 [ qtp1709700394-23 ] INFO [ o.m.driver.connection: 71 ] Opened connection [connectionId{localValue:11, serverValue:1161}] to 127.0.0.1:47017
2019-10-24 19:46:31,561 [ mmonPool-worker-15 ] DEBUG [ o.s.d.m.c.MongoTemplate:2430 ] find using query: { "$or" : [{ "$or" : [{ "_id" : { "$oid" : "593aa90bbe55b4134533f7c7" }, "allRoles" : { "$in" : ["XXXXX"] } }, { "_id" : { "$oid" : "593aa90bbe55b4134533f7c7" }, "allRoles" : { "$in" : ["XXXXX"] } }] }, { "_id" : { "$oid" : "593aa90bbe55b4134533f7c7" }, "allRoles" : { "$in" : ["XXXXXX"] } }] } fields: Document{{}} for class: class com.xxxxxxxx.server.data.model.User in collection: users
2019-10-24 19:46:31,561 [ ommonPool-worker-3 ] DEBUG [ o.s.d.m.c.MongoTemplate:2430 ] find using query: { "$or" : [{ "$or" : [{ "_id" : { "$oid" : "593aa90bbe55b4134533f7c7" }, "allRoles" : { "$in" : ["XXXXX"] } }, { "_id" : { "$oid" : "593aa90bbe55b4134533f7c7" }, "allRoles" : { "$in" : ["XXXXX"] } }] }, { "_id" : { "$oid" : "593aa90bbe55b4134533f7c7" }, "allRoles" : { "$in" : ["XXXXXX"] } }] } fields: Document{{}} for class: class com.xxxxxxxx.server.data.model.User in collection: users
2019-10-24 19:46:31,561 [ qtp1709700394-23 ] DEBUG [ o.s.d.m.c.MongoTemplate:2430 ] find using query: { "$or" : [{ "$or" : [{ "_id" : { "$oid" : "593aa90bbe55b4134533f7c7" }, "allRoles" : { "$in" : ["XXXXX"] } }, { "_id" : { "$oid" : "593aa90bbe55b4134533f7c7" }, "allRoles" : { "$in" : ["XXXXX"] } }] }, { "_id" : { "$oid" : "593aa90bbe55b4134533f7c7" }, "allRoles" : { "$in" : ["XXXXXX"] } }] } fields: Document{{}} for class: class com.xxxxxxxx.server.data.model.User in collection: users
2019-10-24 19:46:31,568 [ ommonPool-worker-3 ] DEBUG [ o.s.d.m.c.MongoTemplate:2356 ] findOne using query: { "id" : "5d91df3e46e0fb0001ff4d0a" } fields: Document{{}} for class: interface com.xxxxxxxx.server.data.model.Call in collection: calls
2019-10-24 19:46:31,571 [ ommonPool-worker-3 ] DEBUG [ o.s.d.m.c.MongoTemplate:2811 ] findOne using query: { "_id" : { "$oid" : "5d91df3e46e0fb0001ff4d0a" } } fields: { } in db.collection: xxxx-debug.calls
2019-10-24 19:46:31,575 [ qtp1709700394-23 ] INFO [ o.m.driver.connection: 71 ] Opened connection [connectionId{localValue:13, serverValue:1162}] to 127.0.0.1:47017
2019-10-24 19:46:31,579 [ qtp1709700394-23 ] DEBUG [ o.s.d.m.c.MongoTemplate:2356 ] findOne using query: { "id" : "5d91df3e46e0fb0001ff4d0a" } fields: Document{{}} for class: interface com.xxxxxxxx.server.data.model.Call in collection: calls
2019-10-24 19:46:31,580 [ qtp1709700394-23 ] DEBUG [ o.s.d.m.c.MongoTemplate:2811 ] findOne using query: { "_id" : { "$oid" : "5d91df3e46e0fb0001ff4d0a" } } fields: { } in db.collection: xxxx-debug.calls
2019-10-24 19:46:31,581 [ ommonPool-worker-3 ] ERROR [ x.x.s.s.PermissionService: 51 ] permission CALL_XXXXXX is unavailable for this resource
org.springframework.data.mapping.model.MappingInstantiationException: Failed to instantiate com.xxxxxxxx.server.data.model.Call using constructor NO_CONSTRUCTOR with arguments
at org.springframework.data.convert.ReflectionEntityInstantiator.createInstance(ReflectionEntityInstantiator.java:67)
at org.springframework.data.convert.ClassGeneratingEntityInstantiator.createInstance(ClassGeneratingEntityInstantiator.java:84)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:274)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:247)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:196)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:192)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:80)
at org.springframework.data.mongodb.core.MongoTemplate$ReadDocumentCallback.doWith(MongoTemplate.java:3024)
at org.springframework.data.mongodb.core.MongoTemplate.executeFindOneInternal(MongoTemplate.java:2633)
at org.springframework.data.mongodb.core.MongoTemplate.doFindOne(MongoTemplate.java:2360)
at org.springframework.data.mongodb.core.MongoTemplate.findById(MongoTemplate.java:850)
at org.springframework.data.mongodb.repository.support.SimpleMongoRepository.findById(SimpleMongoRepository.java:118)
Ilya Zinkevich commented
@Document
public interface Call {
public class TokBoxCall implements Call {
Maybe it helps in invvestigations: only interface is marked with annotation Document
Robert Zilke commented
I had the same issue. The following code solved my problem:
import org.bson.Document;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.ReadingConverter;
@ReadingConverter
public class CallReadingConverter implements Converter<Document, Call> {
@Override
public Call convert(Document source) {
return new TokBoxCall(source.get("key"));
}
}
And register the CallReadingConverter:
@Bean
public MongoCustomConversions customConversions() {
return new MongoCustomConversions(
List.of(
new CallReadingConverter()
)
);
}
Additional information may be found here: https://stackoverflow.com/a/63992362/4471199
@christophstrobl here's a repository with a reproducer for the issue: https://github.com/monosoul/spring-data-mongo-issue/tree/main
The worst thing about it is that the behavior is inconsistent. You can't reproduce it if you save the document first using the same repository. Check SaveAndGetWithRepositoryTest and SaveWithTemplateAndGetWithRepositoryTest.
I also wrote a blog post explaining the issue and providing multiple ways to solve it: https://blog.monosoul.dev/2022/09/16/spring-data-mongodb-polymorphic-fields/
@monosoul thanks for the reproducer. Unless I'm missing something the behaviour is the expected one as documented in the Customizing Type Mapping section of the reference documentation. Providing an initial entity set should solve the issue.
class MongoDbConfig : AbstractMongoClientConfiguration() {
override fun getInitialEntitySet(): Set<Class<*>> {
return setOf(
DocumentWithInterfaceField::class.java,
FieldType.FieldTypeImpl::class.java,
FieldType.OtherFieldTypeImpl::class.java)
}
// ...
Hey @christophstrobl , thanks for the prompt reply!
Providing an initial entity set should solve the issue.
Yeah, initial entity set seems to do the job, although I'd go with something like this:
override fun getInitialEntitySet() = super.getInitialEntitySet().apply {
add(FieldType.FieldTypeImpl::class.java)
add(FieldType.OtherFieldTypeImpl::class.java)
}
Could it have any unexpected side effects though? Is there a chance FieldTypeImpl
and OtherFieldTypeImpl
will be treated as separate collections in mongo?
Unless I'm missing something the behaviour is the expected one as documented in the Customizing Type Mapping section of the reference documentation.
Yeah, the documentation does mention it, that's why I came up with a solution like this:
override fun mappingMongoConverter(
databaseFactory: MongoDatabaseFactory,
customConversions: MongoCustomConversions,
mappingContext: MongoMappingContext
) = super.mappingMongoConverter(databaseFactory, customConversions, mappingContext).apply {
setTypeMapper(
DefaultMongoTypeMapper(
DEFAULT_TYPE_KEY,
listOf(
ConfigurableTypeInformationMapper(
mapOf(
FieldTypeImpl::class.java to "field_type_impl",
OtherFieldTypeImpl::class.java to "other_field_type_impl",
)
),
SimpleTypeInformationMapper(),
)
)
)
}
But the thing is, you wouldn't go to the documentation when having a polymorphic field seems to just work out of the box. Having a test like SaveAndGetWithRepositoryTest
might create an impression it just works. And the worst thing is that even if you save a document without using repository, you might still have other tests that save it with repository, thus affecting the test via a shared context.
I think the behavior should be consistent here: if there is no mapping for the type then it should fail while saving it. It shouldn't only fail when you didn't save a document first before getting another one from the DB.
Just bumped into this issue. getInitialSet()
scans for classes annotated with @Document
. Would it make sense for it to also look for classes annotated with @TypeAlias
? Seems to me like it would solve this issue, as that's essentially what we are doing explicitly when overriding getInitialSet()
.
@TypeAlias
, other than @Document
, is not tied to a specific store, which is insufficient to associate entity classes with the MongoMappingContext
.
Met with this error too. Moreover, it was floating for me, if I first update and then read, then information about the children is added to the cache and everything works fine, but if I make read request right away after startup, then this error appeared. Finally solved it like this:
- set the
@Document
annotations over the types that implement the interface (an important point, if put over the interface, the scanning mechanism will not add children to theinitialEntitySet
, which will result in the error) - added
@EntityScan("your.package.with.models")
, this allows to add information about children to theinitialEntitySet
, and then to the cache, as a result, the error described above does not appear.
initialEntitySet
is a field in the org.springframework.data.mapping.context.AbstractMappingContext
class:
You can put a breakpoint here and see which models are added during the scan:
And the logic of scanning and populating the given context is executed in the given configuration:
Thank you @DobryninVyacheslav for the additional context. EntityScanner
is part of spring-boot and using an AnnotationTypeFilter
that by default does not consider interface
types.
There's however an open issue spring-projects/spring-boot#12828 to allow more fine grained filtering.
At this point I'm inclined to close this issue in favor of the spring-boot one.
Thank you too!