Spring -Data MongoDB问题与作为接口的字段

Posted

技术标签:

【中文标题】Spring -Data MongoDB问题与作为接口的字段【英文标题】:Spring -Data MongoDB issue with field which is an interface 【发布时间】:2013-07-28 10:34:17 【问题描述】:

我正在为 MongoDB 使用 Spring-Data:

版本信息 - org.mongodb.mongo-java-driver 版本 2.10.1, org.springframework.data.spring-data-mongodb 版本 1.2.1.RELEASE。

我有一个案例类似于here 中定义的案例,即(抱歉格式化...):

我刚开始使用 spring-data-mongodb 用 Ja​​va 开发一些应用程序,遇到了一些我无法解决的问题:

我有几个这样的文档 bean:

@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 效果很好,它会将它与 Foo 和 Bar 的“_class”属性。然而,一些人发现 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 ... 

我找到了this answer,它或多或少地表示要避免使用接口。但我很乐意知道是否有更好的选择。

有什么想法吗?

谢谢!

【问题讨论】:

您使用的是哪个版本的 Spring Data MongoDB? 对了,我添加了版本信息——org.mongodb.mongo-java-driver version 2.10.1,org.springframework.data.spring-data-mongodb version 1.2.1.RELEASE。跨度> 感觉就像你偶然发现了jira.springsource.org/browse/DATACMNS-311。您在类路径上有 Spring Data Commons 1.5.1。这是修复错误的版本。 嘿@OliverGierke,是的 - 这就是我的路径中的版本。感谢您的链接,似乎我在仍然使用接口时无法解决这个问题,所以我不得不使用实现类。我希望他们能尽快修复它。 我遇到了同样的问题,最终在我想要的类周围使用了一个包装器。例如,Thing 类有一个 Foo 和一个 Bar(但不是两者都有),然后使用 Thing 来持久化。在回来的路上得到一个东西.isFoo();和 thing.getFoo() 【参考方案1】:

将接口定义为数据对象中的字段确实是个坏主意。

接口表示某个对象做某事的可能性,但不提供有关字段的任何信息。你真的需要使用接口吗?你能避免这种情况吗?即使使用抽象类定义也会更好。

附:当然,无论如何,我的答案都不能被标记为正确答案。

【讨论】:

【参考方案2】:

我的问题和问题类似,但是抛出的异常有点不同:

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

当我的 DB 类的字段之一被声明为接口时,就会发生这种情况。保存此字段很好,但从 MongoDB 读取它时会引发异常。最后我找到了使用org.springframework.core.convert.converter.Converter的解决方案。

两个步骤要做,1.构造一个实现Converter的类; 2. 在 servlet 上下文中注册转换器。是的,您不必修改任何现有代码,例如添加注释。

下面是我的模型类,其中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>

部署应用程序并重试。它应该在接口字段上正确解析来自 MongoDB 的 DBObject。

我的 Spring MongoDB 应用程序的版本是:spring-*-4.1.0 和 spring-data-mongodb-1.6.0。

【讨论】:

【参考方案3】:

我收到了与@victor-wong 相同的错误信息

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

下面的代码解决了这个问题 Spring Boot 2.3.2spring-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

【讨论】:

以上是关于Spring -Data MongoDB问题与作为接口的字段的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 spring security 和 spring boot 对 Google 用户进行身份验证,将 mongoDB 作为存储库?

Spring + Mongodb 与 Null id 问题

Spring with mongo-java-driver 使用 Azure Cosmos DB 作为 MongoDB

Spring @transactional 是不是与 MongoDB 一起使用?

无法将 Spring Boot 与 MongoDb 连接

Spring Boot 连接到运行的 mongo 容器的 MongoDB 副本集