所以我有一个需要存储某些配置信息的应用程序,因此我计划将配置存储为Mongo中的简单JSON文档:
appConfig: {
fizz: true,
buzz: 34
}
这可能映射到Java POJO /实体,如:
public class AppConfig {
private boolean fizz;
private int buzz;
}
等。通常,对于关系数据库,我使用Hibernate / JPA进行从表数据到/从Java实体的O / R映射。我相信最接近表/ Hibernate的JSON / Mongo伴侣是Morphia / GSON组合:使用Morphia驱动从我的Java应用程序到Mongo的连接,然后使用GSON将O / J映射到J / /来自Java POJO /实体。
这里的问题是,随着时间的推移,我的appConfig
文档结构将会发生变化。它可能很简单:
appConfig: {
fizz: true,
buzz: 34
foo: "Hello!"
}
然后需要POJO /实体成为:
public class AppConfig {
private boolean fizz;
private int buzz;
private String foo;
}
但问题是我可能有成千上万的已经存储在Mongo 中的JSON文档中没有foo
个属性。在这种特定情况下,显而易见的解决方案是在属性上设置默认值,如:
public class AppConfig {
private boolean fizz;
private int buzz;
private String foo = "Hello!"
}
但实际上,AppConfig
文档/架构/结构最终可能会发生很大变化,以至于它的形状或形式与其原始设计完全不同。但是踢球者是:我需要向后兼容,并且最好能够更新/转换文档以匹配适当的新模式/结构。
我的问题:这个"版本文件"问题通常会解决吗?
答案 0 :(得分:5)
我通常通过向集合中的每个文档添加版本字段来解决此问题。
AppConfig集合中可能包含多个文档:
{
_id: 1,
fizz: true,
buzz: 34
}
{
_id: 2,
version: 1,
fizz: false,
buzz: 36,
foo: "Hello!"
}
{
_id: 3,
version: 1,
fizz: true,
buzz: 42,
foo: "Goodbye"
}
在上面的例子中,版本1有两个文档,零版本有一个旧文档(在这个模式中,我通常将缺失或null版本字段解释为版本0,因为我总是只添加一次我“通过生产中的文档进行版本控制”。
这种模式的两个原则:
您可以通过检查版本字段并在版本不够新时执行迁移来执行此操作:
DBObject update(DBObject document) {
if (document.getInt("version", 0) < 1) {
document.put("foo", "Hello!"); //add default value for foo
document.put("version", 1);
}
return document;
}
此迁移可以非常轻松地添加具有默认值的字段,重命名字段和删除字段。由于它位于应用程序代码中,因此您可以根据需要进行更复杂的计算。
迁移文档后,您可以通过任何您喜欢的ODM解决方案将其转换为Java对象。此解决方案不再需要担心版本控制,因为它处理的文档都是最新的!
使用Morphia,可以使用@PreLoad注释完成此操作。
两个警告:
有时您可能希望立即将升级后的文档保存回数据库。最常见的原因是迁移成本高昂,迁移不确定或与其他数据库集成,或者您急于升级旧版本。
添加或重命名在查询中用作条件的字段有点棘手。实际上,您可能需要执行多个查询,并统一结果。
在我看来,这种模式突出了MongoDB的一大优势:由于文档在应用程序中进行了版本化,因此您可以在应用程序中无缝迁移数据表示,而无需使用SQL所需的任何脱机“迁移阶段”数据库中。
答案 1 :(得分:2)
JSON反序列化器以一种非常简单的方式解决了这个问题(使用JAVA)
让您的POJO /实体与新字段一起成长。当您将JSON从mongo反序列化为实体时 - 所有缺少的字段都将为空。
mongoDocument v1 : Entity of v3
{
fizz="abc", --> fizz = "abc";
buzz=123 --> buzz = 123;
--> newObj = null;
--> obj_v3 = null;
}
如果您希望遗留服务器使用新的数据库对象,您甚至可以反过来使用它:
mongoDocument v3 : Entity of v1
{
fizz:"abc", --> fizz = "abc";
buzz:123, --> buzz = 123;
newObj:"zzz", -->
obj_v3:"b -->
}
取决于它们是否具有字段 - 它将由反序列化器填充。 请记住,布尔值不是最适合这种情况,因为它们可以默认为false(取决于您使用的解串器)。
因此,除非您正在积极地处理对象的版本控制,否则为什么在构建旧的安全服务器实现时会花费开销,只需几次空检查即可处理任何旧对象。
我希望此提案可以帮助您完成设置
答案 2 :(得分:1)
我想下面的线程会帮助你,虽然它不是关于DB中的文档版本,而是使用spring-data-mongodb完成的,
How to add a final field to an existing spring-data-mongodb document collection?
因此,您可以使用Converter实现基于文档中属性的存在为POJO分配值。
答案 3 :(得分:1)
至少,你有几种选择吗啡。您可以使用版本化的类名,然后依靠morphia使用className属性来获取正确的类版本。然后您的应用程序只需将旧对象迁移到新的类定义。另一个选择是使用@PreLoad
并按摩来自mongo的DBObject
到新形状,然后morphia将DBObject
映射到您的班级。使用类上的版本字段,您可以确定在加载数据时要运行的迁移。从那时起,它将看起来像morphia的新形式,并将无缝地映射。将该配置对象保存回mongo后,它将以新的形式出现,下一次加载不需要运行迁移。