Mongo Java驱动程序找不到接口的公共构造函数。

4

我正在使用https://mongodb.github.io/mongo-java-driver-reactivestreams/1.11/,它似乎在使用https://mongodb.github.io/mongo-java-driver/3.10/。我有许多其他已注册的类可以正常工作。我正在使用https://mongodb.github.io/mongo-java-driver/3.5/bson/pojos/(和Save List of interface objects using mongo driver for java)中提供的建议来处理具有接口字段的数据。然而,我收到以下错误提示。对于其他出现此错误的类,我可以简单地为该类添加一个空构造函数,但是我无法为接口这样做。希望能得到帮助。

Caused by: org.bson.codecs.configuration.CodecConfigurationException: Failed to decode 'SearchCriteria'. Decoding 'filters' errored with: Cannot find a public constructor for 'FilterInterface'.
    at org.bson.codecs.pojo.PojoCodecImpl.decodePropertyModel(PojoCodecImpl.java:222)
    at org.bson.codecs.pojo.PojoCodecImpl.decodeProperties(PojoCodecImpl.java:197)
    at org.bson.codecs.pojo.PojoCodecImpl.decode(PojoCodecImpl.java:121)
    at org.bson.codecs.pojo.PojoCodecImpl.decode(PojoCodecImpl.java:125)
    at org.bson.codecs.pojo.LazyPojoCodec.decode(LazyPojoCodec.java:57)
    at org.bson.codecs.DecoderContext.decodeWithChildContext(DecoderContext.java:93)
    at org.bson.codecs.pojo.PojoCodecImpl.decodePropertyModel(PojoCodecImpl.java:213)
    ... 36 common frames omitted
Caused by: org.bson.codecs.configuration.CodecConfigurationException: Cannot find a public constructor for 'FilterInterface'.
    at org.bson.codecs.pojo.CreatorExecutable.checkHasAnExecutable(CreatorExecutable.java:140)
    at org.bson.codecs.pojo.CreatorExecutable.getInstance(CreatorExecutable.java:107)
    at org.bson.codecs.pojo.InstanceCreatorImpl.<init>(InstanceCreatorImpl.java:40)
    at org.bson.codecs.pojo.InstanceCreatorFactoryImpl.create(InstanceCreatorFactoryImpl.java:28)
    at org.bson.codecs.pojo.ClassModel.getInstanceCreator(ClassModel.java:71)
    at org.bson.codecs.pojo.PojoCodecImpl.decode(PojoCodecImpl.java:120)
    at org.bson.codecs.pojo.PojoCodecImpl.decode(PojoCodecImpl.java:125)
    at org.bson.codecs.pojo.CollectionPropertyCodecProvider$CollectionCodec.decode(CollectionPropertyCodecProvider.java:74)
    at org.bson.codecs.pojo.CollectionPropertyCodecProvider$CollectionCodec.decode(CollectionPropertyCodecProvider.java:43)
    at org.bson.codecs.DecoderContext.decodeWithChildContext(DecoderContext.java:93)
    at org.bson.codecs.pojo.PojoCodecImpl.decodePropertyModel(PojoCodecImpl.java:213)
    ... 42 common frames omitted

以下是我的代码片段:

@BsonDiscriminator
public interface FilterInterface<T> {
    boolean applyOn(T value);

    T getValue();

    ...
}

public abstract class Filter<T> implements FilterInterface<T> {

    public Filter() { }

    public abstract boolean applyOn(T value);

    public abstract T getValue();

    ...
}

public class AddressFilter extends Filter<Address> {

    public AddressFilter() { }

    public boolean applyOn(Address value) {
        return true;
    }

    public Address getValue() {
        return new Address();
    }

    ...
}

public class SearchCriteria {

    public SearchCriteria() { }

    private List<FilterInterface> filters;
}

public static void init() {
    String url = <hidden>;
    MongoClient mongoClient = MongoClients.create(new ConnectionString(url));
    // For POJOs here
    // For interface classes.
    PojoCodecProvider pojoCodecProvider = PojoCodecProvider.builder()
        .conventions(ImmutableList.of(CLASS_AND_PROPERTY_CONVENTION, ANNOTATION_CONVENTION))
        .register(SearchCriteria.class)
        .register(
            ClassModel.builder(FilterInterface.class).enableDiscriminator(true).build(),
            ClassModel.builder(Filter.class).enableDiscriminator(true).build(),
            ClassModel.builder(AddressFilter.class).enableDiscriminator(true).build())
        .automatic(true)
        .build();
    CodecRegistry codecRegistry = CodecRegistries.fromRegistries(
        MongoClientSettings.getDefaultCodecRegistry(),
        CodecRegistries.fromProviders(pojoCodecProvider));
    String dbName = <hidden>;
    mongoDb = mongoClient.getDatabase(dbName).withCodecRegistry(codecRegistry);
}

启用SearchCriteria.class的区分并尝试。 - Ironluca
@Ironluca 很遗憾,那也不起作用。我得到了完全相同的错误。 - user1145925
在C#的Mongo驱动程序中,BsonDiscriminator有一个rootClass=true选项,但是这个特性似乎在Java驱动程序中缺失了... - buræquete
1个回答

4

这个链接提供的示例完美运行。非常感谢那位用户提供的答案。

你可能在 FilterInterface 是类或在使用识别器之前插入了记录。

解决方案: 删除集合并重新填充将能够顺利工作。

如果是生产场景,您可能需要手动为每个文档添加字段 _t

提示:始终使用相同的代码进行序列化和反序列化。

说明:

参考 c-sharp 驱动程序的文档

默认鉴别器约定都使用名为 _t 的元素在 BSON 文档中存储鉴别器值。

如果在启用识别器之前插入了文档,则文档中将没有 _t 字段。当驱动程序开始解码时,它将无法找到并回退到接口 FilterInterface 的默认解码器。

另一方面,如果在 FilterInterface 是类时插入了文档,则 _t 的值将是类的完全限定名称。当解码器开始解码时,它将获取 ClassModel 并尝试创建 FilterInterface 的实例。由于它现在是一个接口,解码器将无法找到构造函数。

这里有一些额外的信息:您可以将字段 _t 更改为任何其他名称,并且可以使用识别器值覆盖类。

@BsonDiscriminator(key = "<field_id>", value = "<value>")

以下是那个答案的修改版本。请先禁用识别器运行它,然后再启用识别器运行它。您会遇到与您的相同错误。然后清理集合并重试。

package org.bson.codecs.chng;

import com.google.common.collect.Lists;
import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import org.bson.codecs.configuration.CodecRegistries;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.codecs.pojo.ClassModel;
import org.bson.codecs.pojo.PojoCodecProvider;
import org.bson.conversions.Bson;

import java.util.Arrays;
import java.util.List;

public class MongoInterfaceTest {

    private static MongoClient mongoClient;

    static {
        init();
    }

    public static void init() {
        try {
            ClassModel<User> userClassModel = ClassModel.builder(User.class).enableDiscriminator(false).build();
            ClassModel<JavaUser> javaUserClassModel = ClassModel.builder(JavaUser.class).enableDiscriminator(false).build();
            ClassModel<PythonUser> pythonUserClassModel = ClassModel.builder(PythonUser.class).enableDiscriminator(false).build();
            ClassModel<TestUser> testUserClassModel = ClassModel.builder(TestUser.class).enableDiscriminator(false).build();

            CodecRegistry pojoCodecRegistry = CodecRegistries.fromRegistries(
                    MongoClientSettings.getDefaultCodecRegistry(),
                    CodecRegistries.fromProviders(
                            PojoCodecProvider.builder()
                                    .register(
                                            userClassModel,
                                            javaUserClassModel,
                                            pythonUserClassModel,
                                            testUserClassModel
                                    )
                                    .build()
                    )
            );

            mongoClient = MongoClients.create(
                    MongoClientSettings.builder()
                            .codecRegistry(pojoCodecRegistry)
                            .applyConnectionString(new ConnectionString(ApplictaionConfig.MONGODB_URL))
                            .applyToConnectionPoolSettings(builder -> {
                                builder.minSize(10);
                            })
                            .build()
            );
        } catch (Exception e) {
            System.out.println("Connection mongodb failed");
            throw new RuntimeException();
        }
    }

    public static void main(String[] args) {
        MongoCollection<TestUser> collection = getMongoCollection("TestUser", TestUser.class);

        JavaUser javaUser = new JavaUser<Integer>("a");
        PythonUser pythonUser = new PythonUser<String>("b", "1");

        TestUser testUser = new TestUser(javaUser.name, javaUser);
        insertOne(collection, testUser);

        testUser = new TestUser(pythonUser.name, pythonUser);
        insertOne(collection, testUser);


        Bson bson = Filters.and(Filters.eq("name", "a"));
        TestUser testUser1 = findFirst(collection, bson);
        System.out.println(testUser1);
        testUser1.users.forEach(x -> System.out.println(x.dev()));

        bson = Filters.and(Filters.eq("name", "b"));
        testUser1 = findFirst(collection, bson);
        System.out.println(testUser1);
        testUser1.users.forEach(x -> System.out.println(x.dev()));

    }

    /**
     * 获得collection对象
     */
    public static <T> MongoCollection<T> getMongoCollection(String collectionName, Class<T> tClass) {
        MongoDatabase mongoDatabase = mongoClient.getDatabase("kikuu");
        MongoCollection<T> collection = mongoDatabase.getCollection(collectionName, tClass);
        return collection;
    }

    public static <T> void insertOne(MongoCollection<T> collection, T document) {
        insertMany(collection, Lists.newArrayList(document));
    }

    public static <T> void insertMany(MongoCollection<T> collection, List<T> documents) {
        collection.insertMany(documents);
    }

    public static <T> T findFirst(MongoCollection<T> collection) {
        return (T) collection.find().first();
    }

    public static <T> T findFirst(MongoCollection<T> collection, Bson bson) {
        return (T) collection.find(bson).first();
    }

    public static interface User<T> {
        String dev();

        T foo();
    }

    public static class JavaUser<T> implements User<T> {
        public String name;


        public JavaUser() {
        }

        public JavaUser(String name) {
            this.name = name;
        }

        @Override
        public String dev() {
            return "java";
        }

        @Override
        public String toString() {
            return "JavaUser{" +
                    "name='" + name + '\'' +
                    '}';
        }

        @Override
        public T foo() {
            return null;
        }
    }

    public static class PythonUser<T> implements User<T> {
        public String name;
        public String age;

        public PythonUser() {
        }

        public PythonUser(String name, String age) {
            this.name = name;
            this.age = age;
        }

        @Override
        public String dev() {
            return "python";
        }

        @Override
        public String toString() {
            return "PythonUser{" +
                    "name='" + name + '\'' +
                    ", age='" + age + '\'' +
                    '}';
        }

        @Override
        public T foo() {
            return null;
        }
    }

    public static class TestUser {
        public String name;
        public List<User> users;

        public TestUser() {
        }

        public TestUser(String name, User... users) {
            this.name = name;
            this.users = Arrays.asList(users);
        }

        @Override
        public String toString() {
            return "TestUser{" +
                    "name='" + name + '\'' +
                    ", user=" + users +
                    '}';
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接