Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/327.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/mongodb/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java MongoDB对象版本控制_Java_Mongodb_Diff_Versioning_Spring Data - Fatal编程技术网

Java MongoDB对象版本控制

Java MongoDB对象版本控制,java,mongodb,diff,versioning,spring-data,Java,Mongodb,Diff,Versioning,Spring Data,我需要对存储在面向文档数据库(MongoDB)中的(简单的)Java对象图进行版本控制。对于关系数据库和Hibernate,我发现了这些可能性,并对此感到非常惊讶。是否有类似的东西可用于Spring数据文档 我发现它概述了我关于存储对象版本的想法(以及更多…),我当前的实现工作原理类似,它将对象的副本存储在单独的历史记录集合中,并带有时间戳,但我想对此进行改进以节省存储空间。因此,我认为我需要在对象树上实现“diff”操作和重建旧对象的“merge”操作。有没有图书馆可以帮忙 编辑: 任何Mon

我需要对存储在面向文档数据库(MongoDB)中的(简单的)Java对象图进行版本控制。对于关系数据库和Hibernate,我发现了这些可能性,并对此感到非常惊讶。是否有类似的东西可用于Spring数据文档

我发现它概述了我关于存储对象版本的想法(以及更多…),我当前的实现工作原理类似,它将对象的副本存储在单独的历史记录集合中,并带有时间戳,但我想对此进行改进以节省存储空间。因此,我认为我需要在对象树上实现“diff”操作和重建旧对象的“merge”操作。有没有图书馆可以帮忙

编辑: 任何MongoDB和版本控制经验都将受到高度赞赏!我认为很可能不会有Spring数据解决方案。

我们使用的是一个基本实体(我们在其中设置Id、创建+上次更改日期等)。在此基础上,我们使用了一种通用的持久性方法,该方法如下所示:

@Override
public <E extends BaseEntity> ObjectId persist(E entity) {
    delta(entity);
    mongoDataStore.save(entity);
    return entity.getId();
}

希望这能帮助您入门:-)

这就是我最终实现MongoDB实体版本控制的原因。感谢StackOverflow社区的帮助

  • 在单独的历史记录集合中为每个实体保留更改日志
  • 为了避免保存大量数据,历史记录集合不存储完整的实例,而只存储第一个版本和版本之间的差异。(您甚至可以省略第一个版本,并从实体的主集合中的当前版本“向后”重构版本。)
  • 用于生成对象差异
  • 为了能够正确使用集合,需要实现实体的
    equals
    方法,以便测试数据库主键而不是子属性。(否则,JavaObjectDiff将无法识别集合元素中的属性更改。)
以下是我用于版本控制的实体(删除了getter/setter等):

编辑:以下是访客代码:

public class MongoDiffHistoryChangeVisitor implements Visitor {

private MongoDiffHistoryChange change;
private Object working;
private Object base;

public MongoDiffHistoryChangeVisitor(MongoDiffHistoryChange change, Object working, Object base) {
    this.change = change;
    this.working = working;
    this.base = base;
}

public void accept(Node node, Visit visit) {
    if (node.isRootNode() && !node.hasChanges() ||
        node.hasChanges() && node.getChildren().isEmpty()) {
        MongoDiffHistoryChangeItem diffItem = new MongoDiffHistoryChangeItem();
        diffItem.setPath(node.getPropertyPath().toString());
        diffItem.setState(node.getState());

        if (node.getState() != State.UNTOUCHED) {
            diffItem.setBase(node.canonicalGet(base));
            diffItem.setModified(node.canonicalGet(working));
        }

        if (change.getItems() == null)
            change.setItems(new ArrayList<MongoDiffHistoryChangeItem>());
        change.getItems().add(diffItem);
    }
}

}
公共类MongoDiffHistoryChangeVisitor实现了Visitor{
私有蒙哥迪夫历史变迁;
私人物品工作;
私有对象库;
公共MongoDiffHistoryChangeVisitor(MongoDiffHistoryChange更改、对象工作、对象库){
改变=改变;
这个。工作=工作;
this.base=base;
}
公共作废接受(节点、访问){
if(node.isRootNode()&&!node.hasChanges()||
node.hasChanges()&&node.getChildren().isEmpty()){
MongoDiffHistoryChangeItem diffItem=新建MongoDiffHistoryChangeItem();
setPath(node.getPropertyPath().toString());
diffItem.setState(node.getState());
if(node.getState()!=State.UNTOUCHED){
setBase(node.canonicalGet(base));
setModified(node.canonicalGet(工作));
}
if(change.getItems()==null)
change.setItems(新的ArrayList());
change.getItems().add(diffItem);
}
}
}

看来Javers是这项工作的合适工具,请参阅


Javers在概念上是一个域对象版本控制的VCS,由JSON和MongoDB支持,而不是完全版本控制,但我们已经实现了一个小型审计系统——记录谁将旧值更改为新值。我们使用的是Morphia的
prePersist()
方法(它只适用于完整的实体保存,而不适用于特定的更新)。可以提供一些代码示例,但并不复杂…感谢您的评论!我对演示您的解决方案的更多细节非常感兴趣。只跟踪完整的实体保存是完全可以的:这也是我们的主要用例。一个非常有趣的点是比较新旧实体的方式,识别更改的属性。我在这里查看了图形比较框架,但没有找到一个快速简单的解决方案。非常感谢您提供的示例。我还发现了一篇关于java对象diff()的帖子,其中提到了这个库:-也许我可以用这个diff算法“增加”您的解决方案。我想把这个问题留一段时间,也许还有其他想法。有趣的项目,期待你的解决方案。在此期间,我们仍希望进行表决;-)事实上,我不得不修改我先前的评论。我尝试使用Javers,但发现它不可行,因为它总是从基本版本加上所有更改来构造当前对象,这使得读取时间大约是将文档的最新版本存储在某处时的20倍。由于获取文档的最新版本是一种主要用例,在我看来,这是一种阻碍。对于今年以后查看的其他人来说,Visitor::accept已被重命名为Visitor::node。
@Entity(value = "delta", noClassnameStored = true)
public final class DeltaEntity extends BaseEntity {
    private static final long serialVersionUID = -2770175650780701908L;

    private String entityClass; // Do not call this className as Morphia will
                            // try to work some magic on this automatically
    private ObjectId entityId;
    private String entityUuid;
    private String userEmail;
    private String delta;

    public DeltaEntity() {
        super();
    }

    public DeltaEntity(final String entityClass, final ObjectId entityId, final String entityUuid,
            final String userEmail, final String delta) {
        this();
        this.entityClass = entityClass;
        this.entityId = entityId;
        this.entityUuid = entityUuid;
        this.userEmail = userEmail;
        this.delta = delta;
    }
// This entity is stored once (1:1) per entity that is to be versioned
// in an own collection
public class MongoDiffHistoryEntry {
    /* history id */
    private String id;

    /* reference to original entity */
    private String objectId;

    /* copy of original entity (first version) */
    private Object originalObject;

    /* differences collection */
    private List<MongoDiffHistoryChange> differences;

    /* delete flag */
    private boolean deleted;
}

// changeset for a single version
public class MongoDiffHistoryChange {
    private Date historyDate;
    private List<MongoDiffHistoryChangeItem> items;
}

// a single property change
public class MongoDiffHistoryChangeItem {
    /* path to changed property (PropertyPath) */
    private String path;

    /* change state (NEW, CHANGED, REMOVED etc.) */
    private Node.State state;

    /* original value (empty for NEW) */
    private Object base;

    /* new value (empty for REMOVED) */
    private Object modified;
}
private void saveChangeHistory(Object working, Object base) {
    assert working != null && base != null;
    assert working.getClass().equals(base.getClass());

    String baseId = ObjectUtil.getPrimaryKeyValue(base).toString();
    String workingId = ObjectUtil.getPrimaryKeyValue(working).toString();
    assert baseId != null && workingId != null && baseId.equals(workingId);

    MongoDiffHistoryEntry entry = getObjectHistory(base.getClass(), baseId);
    if (entry == null) {
        //throw new RuntimeException("history not found: " + base.getClass().getName() + "#" + baseId);
        logger.warn("history lost - create new base history record: {}#{}", base.getClass().getName(), baseId);
        saveNewHistory(base);
        saveHistory(working, base);
        return;
    }

    final MongoDiffHistoryChange change = new MongoDiffHistoryChange();
    change.setHistoryDate(new Date());
    change.setItems(new ArrayList<MongoDiffHistoryChangeItem>());

    ObjectDiffer differ = ObjectDifferFactory.getInstance();
    Node root = differ.compare(working, base);
    root.visit(new MongoDiffHistoryChangeVisitor(change, working, base));

    if (entry.getDifferences() == null)
        entry.setDifferences(new ArrayList<MongoDiffHistoryChange>());
    entry.getDifferences().add(change);

    mongoTemplate.save(entry, getHistoryCollectionName(working.getClass()));
}
{
  "_id" : ObjectId("5040a9e73c75ad7e3590e538"),
  "_class" : "MongoDiffHistoryEntry",
  "objectId" : "5034c7a83c75c52dddcbd554",
  "originalObject" : {
      BLABLABLA, including sections collection etc.
  },
  "differences" : [{
      "historyDate" : ISODate("2012-08-31T12:11:19.667Z"),
      "items" : [{
          "path" : "/sections[LetterSection@116a3de]",
          "state" : "ADDED",
          "modified" : {
            "_class" : "LetterSection",
            "_id" : ObjectId("5034c7a83c75c52dddcbd556"),
            "letterId" : "5034c7a83c75c52dddcbd554",
            "sectionIndex" : 2,
            "stringContent" : "BLABLA",
            "contentMimetype" : "text/plain",
            "sectionConfiguration" : "BLUBB"
          }
        }, {
          "path" : "/sections[LetterSection@19546ee]",
          "state" : "REMOVED",
          "base" : {
            "_class" : "LetterSection",
            "_id" : ObjectId("5034c7a83c75c52dddcbd556"),
            "letterId" : "5034c7a83c75c52dddcbd554",
            "sectionIndex" : 2,
            "stringContent" : "BLABLABLA",
            "contentMimetype" : "text/plain",
            "sectionConfiguration" : "BLUBB"
          }
        }]
    }, {
      "historyDate" : ISODate("2012-08-31T13:15:32.574Z"),
      "items" : [{
          "path" : "/sections[LetterSection@44a38a]/stringContent",
          "state" : "CHANGED",
          "base" : "blub5",
          "modified" : "blub6"
        }]
    },
    }],
  "deleted" : false
}
public class MongoDiffHistoryChangeVisitor implements Visitor {

private MongoDiffHistoryChange change;
private Object working;
private Object base;

public MongoDiffHistoryChangeVisitor(MongoDiffHistoryChange change, Object working, Object base) {
    this.change = change;
    this.working = working;
    this.base = base;
}

public void accept(Node node, Visit visit) {
    if (node.isRootNode() && !node.hasChanges() ||
        node.hasChanges() && node.getChildren().isEmpty()) {
        MongoDiffHistoryChangeItem diffItem = new MongoDiffHistoryChangeItem();
        diffItem.setPath(node.getPropertyPath().toString());
        diffItem.setState(node.getState());

        if (node.getState() != State.UNTOUCHED) {
            diffItem.setBase(node.canonicalGet(base));
            diffItem.setModified(node.canonicalGet(working));
        }

        if (change.getItems() == null)
            change.setItems(new ArrayList<MongoDiffHistoryChangeItem>());
        change.getItems().add(diffItem);
    }
}

}