spring-data-mongodb icon indicating copy to clipboard operation
spring-data-mongodb copied to clipboard

BeanInstantiationException: Failed to instantiate []: Specified class is an interface [DATAMONGO-2391]

Open spring-projects-issues opened this issue 5 years ago • 15 comments

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

spring-projects-issues avatar Oct 15 '19 07:10 spring-projects-issues

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?

spring-projects-issues avatar Oct 17 '19 13:10 spring-projects-issues

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

spring-projects-issues avatar Oct 24 '19 15:10 spring-projects-issues

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);
}
}

spring-projects-issues avatar Oct 24 '19 16:10 spring-projects-issues

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

 

spring-projects-issues avatar Oct 24 '19 17:10 spring-projects-issues

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)

spring-projects-issues avatar Oct 24 '19 17:10 spring-projects-issues

Ilya Zinkevich commented

@Document
public interface Call {
 public class TokBoxCall implements Call {

Maybe it helps in invvestigations: only interface is marked with annotation Document

spring-projects-issues avatar Oct 29 '19 12:10 spring-projects-issues

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

spring-projects-issues avatar Sep 21 '20 12:09 spring-projects-issues

@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 avatar Sep 17 '22 09:09 monosoul

@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)
    }

    // ...

christophstrobl avatar Sep 19 '22 07:09 christophstrobl

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.

monosoul avatar Sep 19 '22 11:09 monosoul

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().

ehardy avatar Mar 24 '23 13:03 ehardy

@TypeAlias, other than @Document, is not tied to a specific store, which is insufficient to associate entity classes with the MongoMappingContext.

christophstrobl avatar Mar 24 '23 14:03 christophstrobl

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 the initialEntitySet, which will result in the error)
  • added @EntityScan("your.package.with.models"), this allows to add information about children to the initialEntitySet, 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: image You can put a breakpoint here and see which models are added during the scan: image And the logic of scanning and populating the given context is executed in the given configuration: image

viacheslav-dobrynin avatar Jul 27 '23 06:07 viacheslav-dobrynin

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.

christophstrobl avatar Jul 27 '23 10:07 christophstrobl

Thank you too!

viacheslav-dobrynin avatar Jul 27 '23 11:07 viacheslav-dobrynin