Spring Data,Mongo和@TypeAlias:阅读不起作用

Dav*_*ler 3 java mongodb spring-data

问题

一段时间后,我开始使用MongoDB和Spring Data.我保留了大部分默认功能,因此我的所有文档都存储在MongoDB中,并带有_class指向实体的完全限定类名的字段.

马上就没有"闻到"我的权利,但我一个人离开了.直到最近,当我重构了一堆代码时,突然间我的所有文档都无法从MongoDB中读回并转换为它们的(重构/重命名)Java实体.我很快意识到这是因为现在有一个完全合格的类名不匹配.我也很快意识到 - 鉴于我将来可能会再次重构 - 如果我不希望我的所有数据都变得无法使用,我需要找出其他的东西.

我试过的

这就是我正在做的事情,但我已经碰壁了.我我需要做以下事情:

  • 使用@TypeAlias("ta")"ta"是唯一的稳定字符串来标注每个实体.
  • 配置和使用不同TypeInformationMapper的Spring Data,以便在将文档转换回Java实体时使用; 例如,它需要知道"widget.foo"的类型别名是指com.myapp.document.FooWidget.

我确定我应该使用一种TypeInformationMapper类型org.springframework.data.convert.MappingContextTypeInformationMapper.据推测,MappingContextTypeInformationMapper将扫描我的实体/文档以查找@ TypeAlias的文档并存储别名 - > to->类映射.但我无法将其传递给我的MappingMongoConverter; 我必须传递MongoTypeMapper的子类型.所以我正在配置一个DefaultMongoTypeMapper,并将一个List MappingContextTypeInformationMapper作为其"mappers"构造函数arg 传递.

这是我的spring XML配置的相关部分:

<bean id="mongoTypeMapper" class="org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper">
    <constructor-arg name="typeKey" value="_class"></constructor-arg>
    <constructor-arg name="mappers">
        <list>
            <ref bean="mappingContextTypeMapper" />
        </list>
    </constructor-arg> 
</bean>

<bean id="mappingContextTypeMapper" class="org.springframework.data.convert.MappingContextTypeInformationMapper">
    <constructor-arg ref="mappingContext" />
</bean>

<bean id="mappingMongoConverter"
    class="org.springframework.data.mongodb.core.convert.MappingMongoConverter">
    <constructor-arg ref="mongoDbFactory" />
    <constructor-arg ref="mappingContext" />
    <property name="mapKeyDotReplacement" value="__dot__" />
    <property name="typeMapper" ref="mongoTypeMapper"/>
 </bean>

 <bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
    <constructor-arg ref="mongoDbFactory" />
    <constructor-arg ref="mappingMongoConverter" />
 </bean>
Run Code Online (Sandbox Code Playgroud)

这是一个示例实体/文档:

@Document(collection="widget")
@TypeAlias("widget.foo")
public class FooWidget extends Widget {

    // ...

}
Run Code Online (Sandbox Code Playgroud)

一个重要的注意事项是任何这样的"Widget"实体都存储为Mongo中的嵌套文档.所以实际上你不会在我的MongoDB实例中找到一个填充的"Widget"集合.相反,更高级别的"Page"类可以包含多个"小部件",如下所示:

@Document(collection="page")
@TypeAlias("page")
public class Page extends BaseDocument {

    // ...

    private List<Widget> widgets = new ArrayList<Widget>();

}
Run Code Online (Sandbox Code Playgroud)

错误我坚持了下来

会发生什么是我可以在Mongo中保存一个页面以及一些嵌套的Widgets.但是,当我尝试阅读所述页面时,我得到如下内容:

org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.myapp.document.Widget]: Is it an abstract class?
Run Code Online (Sandbox Code Playgroud)

我确实可以在Mongo中看到包含的页面"_class" : "page",嵌套的小部件也包含"_class" : "widget.foo" 它,看起来好像没有反向应用映射.

有什么我可能会失踪的吗?

Dav*_*ler 5

我花了很多时间使用我的调试器和Spring Data源代码,并且我了解到Spring Data并不像它应该具有的多态性一样好,特别是考虑到NoSQL解决方案的无模式特性, MongoDB的.但最终我所做的就是编写自己的类型映射器,这并不太难.

主要的问题是,在我的Page文档中读取时,Spring Data使用的默认映射器会看到一个名为widgets的集合,然后查阅Page类以确定指向List的小部件,然后查阅Widget类以查找@ TypeAlias信息.我需要的是一个映射器,它可以预先扫描我的持久性实体,并存储一个别名到类的映射供以后使用.这就是我的自定义类型映射器所做的.

我写了一篇讨论细节博客文章.


Chr*_*e S 5

在默认设置中,MappingMongoConverter会创建一个DefaultMongoTypeMapper,然后创建一个MappingContextTypeInformationMapper.

最后一个类负责维护别名typeMap之间的缓存TypeInformation

该缓存填充在两个地方:

  1. 在构造函数中,对于每个 mappingContext.getPersistentEntities()
  2. 编写别名类型的对象时。

因此,如果您想确保别名在任何上下文中都能被识别,您需要确保所有别名实体都是mappingContext.getPersistentEntities().

您如何做到这一点取决于您的配置。例如:

  • 如果您正在使用AbstractMongoConfiguration,则可以覆盖它getMappingBasePackage()以返回包含所有实体的包的名称。
  • 如果您使用的是 spring boot,则可以使用@EntityScan来声明要扫描实体的包
  • 在任何情况下,您始终可以使用自定义集(来自静态列表或自定义扫描)对其进行配置 mongoMappingContext.setInitialEntitySet()

一方面,对于要通过扫描发现的实体,必须使用@Document或进行注释@Persitent

更多信息可以在spring-data-commons 开发者指南中找到