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

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

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

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

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

У меня есть пара документов beans следующим образом:

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

Сохранение панели работает очень хорошо, это спасло бы ее в монго вместе с Атрибут "_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).

В моем случае у меня есть интерфейс, а не 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 ... 
}

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

Любые идеи?

Спасибо!

Ответ 1

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

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

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

TWO​​strong > шаги, 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.

Ответ 2

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

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

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