Spring -Data MongoDB проблема с полем, которое является интерфейсом

Я использую Spring-Data для MongoDB:

Информация о версии - org.mongodb.mongo-java-driver версии 2.10.1, org.springframework.data.spring-data-mongodb версии 1.2.1.RELEASE.

У меня есть случай, похожий на тот, который определен в здесь, который (извините за форматирование ...):

Я только начал разрабатывать какое-то приложение на Java с помощью spring-data-mongodb и столкнулся с проблемой, которую не смог решить:

У меня есть пара таких компонентов документа:

@Document(collection="myBeanBar")
public class BarImpl implements Bar {
    String id;
    Foo foo;
    // More fields and methods ... 
}

@Document
public class FooImpl implements Foo {
    String id;
    String someField;
    // some more fields and methods ...
} 

И у меня есть класс репозитория с методом, который просто вызывает поиск, подобный этому:

public List<? extends Bar> findByFooField(final String fieldValue) {
    Query query = Query.query(Criteria.where("foo.someField").is(fieldValue));
    return getMongoOperations().find(query, BarImpl.class);
} 

Сохранение Bar работает нормально, оно сохранит его в mongo вместе с атрибутом «_class» как для Foo, так и для Bar. Однако поиск по некоторому атрибуту в Foo вызовет такое исключение:

Exception in thread "main" java.lang.IllegalArgumentException: No
property someField found on test.Foo!
    at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentPropertyPath(AbstractMappingContext.java:225)
    at org.springframework.data.mongodb.core.convert.QueryMapper.getPath(QueryMapper.java:202)
    at org.springframework.data.mongodb.core.convert.QueryMapper.getTargetProperty(QueryMapper.java:190)
    at org.springframework.data.mongodb.core.convert.QueryMapper.getMappedObject(QueryMapper.java:86)
    at org.springframework.data.mongodb.core.MongoTemplate.doFind(MongoTemplate.java:1336)
    at org.springframework.data.mongodb.core.MongoTemplate.doFind(MongoTemplate.java:1322)
    at org.springframework.data.mongodb.core.MongoTemplate.find(MongoTemplate.java:495)
    at org.springframework.data.mongodb.core.MongoTemplate.find(MongoTemplate.java:486)

Было предложено решение использовать аннотацию @TypeAlias ​​для абстрактного класса, которая сообщала фреймворку использовать конкретную реализацию (в данном случае FooImpl).

В моем случае у меня есть элементы interface вместо элементов abstract:

@Document(collection="myBeanBar")
public class BarImpl implements Bar {
    String id;
    IFoo foo;
    // More fields and methods ...
}

Я очень неохотно помещаю аннотацию к интерфейсу IFoo, которая даст реализацию по умолчанию, вместо этого я хотел бы сообщить фреймворку, какова реализация этого поля по умолчанию в контекст реализуемого класса BarImpl, аналогичный @JsonTypeInfo:

@Document(collection="myBeanBar") 
public class BarImpl implements Bar {
    String id;    

    @JsonTypeInfo(use = Id.CLASS, defaultImpl = FooImpl.class)
    IFoo foo; 

    // More fields and methods ... 
}

Я нашел этот ответ, который более или менее говорит о том, что следует избегать используя интерфейсы. но я был бы рад узнать, нет ли лучшего варианта.

Любые идеи?

Спасибо!


person Ido Cohn    schedule 28.07.2013    source источник
comment
Какую версию Spring Data MongoDB вы используете?   -  person Oliver Drotbohm    schedule 30.07.2013
comment
Правильно, я добавил информацию о версии - org.mongodb.mongo-java-driver версии 2.10.1, org.springframework.data.spring-data-mongodb версии 1.2.1.RELEASE.   -  person Ido Cohn    schedule 30.07.2013
comment
Похоже, вы наткнулись на jira.springsource.org/browse/DATACMNS-311. У вас есть Spring Data Commons 1.5.1 в пути к классам. Это версия, в которой была исправлена ​​ошибка.   -  person Oliver Drotbohm    schedule 30.07.2013
comment
Привет, @OliverGierke, да, это та версия, которая есть у меня на пути. Спасибо за ссылку, похоже, я не могу исправить это, пока все еще использую интерфейсы, поэтому мне пришлось вместо этого использовать класс реализации. Надеюсь, они скоро это исправят.   -  person Ido Cohn    schedule 01.08.2013
comment
У меня была такая же проблема, и в итоге я использовал оболочку для класса, который мне нужен. Например, у класса Thing есть Foo и Bar (но не оба сразу), затем используйте Thing для сохранения. На обратном пути возьмите вещь .isFoo (); и thing.getFoo ()   -  person Alexandre Santos    schedule 15.04.2014
comment
Не могли бы вы начать добавлять свой пакет org.springframework.data в отладку? Вы сможете увидеть сгенерированный запрос и сравнить этот запрос с вашей схемой в вашем mongo.   -  person Patouche    schedule 29.05.2014


Ответы (3)


Моя проблема аналогична вопросу, но выбранное исключение немного отличается:

Could not instantiate bean class [class name]: Specified class is an interface

Это происходит, когда одно из полей моего класса БД объявлено как интерфейс. Сохранить это поле можно, но при чтении из MongoDB возникает исключение. Наконец я нашел решение, использующее org.springframework.core.convert.converter.Converter.

Необходимо выполнить ДВА шага: 1. создать класс, реализующий Converter; 2. Зарегистрируйте преобразователь в контексте сервлета. И ДА, вам не нужно изменять какой-либо существующий код, например добавлять аннотации.

Ниже мой модельный класс, где поле Data является интерфейсом:

@Document(collection="record")
public class Record {
    @Id
    private String id;

    // Data is an interface
    private Data data;

    // And some other fields and setter/getter methods of them
}

Конвертер:

@ReadingConverter
public class DataReadConverter implements Converter<DBObject, Data> {
    @Override
    public Data convert(DBObject source) {
        // Your implementation to parse the DBObject,
        // this object can be BasicDBObject or BasicDBList,
        // and return an object instance that implements Data.

        return null;
    }
}

Последнее, что нужно сделать, это зарегистрировать конвертер, моя конфигурация находится в xml:

<mongo:mongo id="mongo" />

<mongo:db-factory mongo-ref="mongo" dbname="example" />

<mongo:mapping-converter>
    <mongo:custom-converters>
        <mongo:converter>
            <beans:bean class="com.example.DataReadConverter" />
        </mongo:converter>
    </mongo:custom-converters>
</mongo:mapping-converter>

<beans:bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
    <beans:constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
    <beans:constructor-arg name="mongoConverter" ref="mappingConverter" />
</beans:bean>

Разверните приложение и попробуйте еще раз. Он должен правильно проанализировать DBObject из MongoDB в поле интерфейса.

Версия моего приложения Spring MongoDB: spring - * - 4.1.0 и spring-data-mongodb-1.6.0.

person Victor Wong    schedule 24.11.2014

Это действительно плохая идея определять интерфейсы как поле в объекте данных.

Интерфейс означает возможность какого-либо объекта что-то делать, но не предоставляет никакой информации о полях. Вам действительно нужно использовать интерфейсы? Вы можете этого избежать? Даже использование определения абстрактного класса будет лучшей идеей.

P.S. Конечно, мой ответ ни в коем случае нельзя назвать правильным.

person iMysak    schedule 01.08.2014

У меня было то же сообщение об ошибке, что и у @ victor-wong

Could not instantiate bean class [class name]: Specified class is an interface

Следующий код решает проблему с Spring Boot 2.3.2 и spring-data-mongodb 3.0.2.

Конвертер:

import org.bson.Document;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.ReadingConverter;

@ReadingConverter
public class DataReadConverter implements Converter<Document, Data> {

    @Override
    public Data convert(Document source) {

        return new DataImpl(source.get("key"));
    }
}

Последнее, что нужно сделать, это зарегистрировать конвертер.

    @Bean
    public MongoCustomConversions customConversions() {
        return new MongoCustomConversions(
                List.of(
                        new DataReadConverter()
                )
        );
    }

Дополнительную информацию можно найти здесь: https://jira.spring.io/browse/DATAMONGO-2391

person RoBeaToZ    schedule 21.09.2020