[英]Spring -Data MongoDB issue with field which is an interface
我將 Spring-Data 用於 MongoDB:
版本信息 - org.mongodb.mongo-java-driver 版本 2.10.1,org.springframework.data.spring-data-mongodb 版本 1.2.1.RELEASE。
我有一個與此處定義的案例類似的案例,即(抱歉格式化...):
我剛開始使用 spring-data-mongodb 在 Java 中開發一些應用程序,遇到了一些我無法解決的問題:
我有幾個這樣的文檔 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 ...
}
我有一個存儲庫 class ,它的方法簡單地調用類似於這樣的查找:
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”屬性一起保存在 mongo 中。 但是,通過 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)
給出的解決方案是在抽象 class 上使用 @TypeAlias 注釋,它告訴框架使用特定的實現(在本例中為 FooImpl )。
就我而言,我有接口成員,而不是抽象成員:
@Document(collection="myBeanBar")
public class BarImpl implements Bar {
String id;
IFoo foo;
// More fields and methods ...
}
我很不情願在接口IFoo上添加一個會給出默認實現的注解,相反我想告訴框架在實現BarImpl class 的上下文中這個字段的默認實現是什么,類似於@JsonTypeInfo:
@Document(collection="myBeanBar")
public class BarImpl implements Bar {
String id;
@JsonTypeInfo(use = Id.CLASS, defaultImpl = FooImpl.class)
IFoo foo;
// More fields and methods ...
}
我找到了這個答案,它或多或少地說是為了避免使用接口。 但我很高興知道是否有更好的選擇。
有任何想法嗎?
謝謝!
我的問題和問題類似,但是拋出的異常有點不同:
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。
將接口定義為數據對象中的字段確實是個壞主意。
接口意味着某個對象做某事的可能性,但不提供有關字段的任何信息。 你真的需要使用接口嗎? 你能避免這種情況嗎? 即使使用抽象類定義也會更好。
PS當然,我的答案無論如何都不能被標記為正確答案。
我收到了與@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
雖然其他答案中提供的帶有轉換器的解決方案將起作用,但它需要您為每個多態文件編寫一個轉換器。 這是乏味且容易出錯的,因為它要求您在每次添加新的多態字段或更改現有模型時牢記這些轉換器。
無需編寫轉換器,您只需配置類型映射即可。 此外,您可以使其自動工作。 就是這樣:
首先我們需要反射庫。 在 此處為您選擇的構建工具選擇最新版本。
例如 Gradle:
implementation("org.reflections:reflections:0.10.2")
現在我們需要對DefaultMongoTypeMapper
進行一個小擴展,以使其易於配置和實例化。 這是它在 Kotlin 中的外觀:
class ReflectiveMongoTypeMapper( private val reflections: Reflections = Reflections("com.example") ): DefaultMongoTypeMapper( DEFAULT_TYPE_KEY, listOf( ConfigurableTypeInformationMapper( reflections.getTypesAnnotatedWith(TypeAlias::class.java).associateWith { clazz -> getAnnotation(clazz, TypeAlias::class.java).,,value } ), SimpleTypeInformationMapper(), ) )
其中com.example
是您的基礎 package 或 package 與 Z206E3718AF092FD1DZE12F092FD1DZCDACC8Z 型號。
這樣,我們將找到所有使用@TypeAlias
注釋的類,並將別名注冊到類型映射。
接下來我們需要稍微調整一下應用的 mongo 配置。 配置必須擴展AbstractMongoClientConfiguration
並且我們需要覆蓋方法mappingMongoConverter
以使用我們之前創建的映射器。 它應該如下所示:
override fun mappingMongoConverter( databaseFactory: MongoDatabaseFactory, customConversions: MongoCustomConversions, mappingContext: MongoMappingContext, ) = super.mappingMongoConverter(databaseFactory, customConversions, mappingContext).apply { setTypeMapper(ReflectiveMongoTypeMapper()) }
完畢!
現在,所有類型映射的別名都將在上下文啟動時自動注冊,並且所有多態字段都可以正常工作。 無需編寫和維護轉換器。
此外,這是一篇博文,您可以在其中閱讀有關此問題的根本原因以及檢查其他解決方法(以防您不想依賴反射): https://blog.monosoul.dev/ 2022/09/16/spring-data-mongodb-polymorphic-fields/
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.