Java MongoDB 对象版本控制

2022-01-18 00:00:00 mongodb diff versioning java spring-data

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

I need to do versioning on (simple) Java object graphs stored in a document-oriented database (MongoDB). For relational databases and Hibernate, I discovered Envers and am very amazed about the possibilities. Is there something similar that can be used with Spring Data Documents?

我发现 这篇文章概述了我的想法(以及更多...)关于存储对象版本,我当前的实现工作类似,因为它将对象的副本存储在带有时间戳的单独历史集合中,但我想改进它以节省存储空间.因此,我认为我需要在对象树上实现差异"操作和重构旧对象的合并"操作.有没有图书馆可以帮助解决这个问题?

I found this post outlining the thoughts I had (and more...) about storing the object versions, and my current implementation works similar in that it stores copies of the objects in a separate history collection with a timestamp, but I would like to improve this to save storage space. Therefore, I think I need to implement both a "diff" operation on object trees and a "merge" operation for reconstructing old objects. Are there any libraries out there helping with this?

编辑:任何使用 MongoDB 和版本控制的经验都非常感谢!我认为很可能不会有 Spring Data 解决方案.

Edit: Any experiences with MongoDB and versioning highly appreciated! I see most probably there won't be a Spring Data solution.

推荐答案

我们正在使用一个基础实体(我们在其中设置 Id、创建 + 上次更改日期......).在此基础上,我们使用了一个通用的持久化方法,看起来像这样:

We're using a base entity (where we set the Id, creation + last change dates,...). Building upon this we're using a generic persistence method, which looks something like this:

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

delta 方法看起来像这样(我会尽量使它尽可能通用):

The delta method looks like this (I'll try to make this as generic as possible):

protected <E extends BaseEntity> void delta(E newEntity) {

    // If the entity is null or has no ID, it hasn't been persisted before,
    // so there's no delta to calculate
    if ((newEntity == null) || (newEntity.getId() == null)) {
        return;
    }

    // Get the original entity
    @SuppressWarnings("unchecked")
    E oldEntity = (E) mongoDataStore.get(newEntity.getClass(), newEntity.getId()); 

    // Ensure that the old entity isn't null
    if (oldEntity == null) {
        LOG.error("Tried to compare and persist null objects - this is not allowed");
        return;
    }

    // Get the current user and ensure it is not null
    String email = ...;

    // Calculate the difference
    // We need to fetch the fields from the parent entity as well as they
    // are not automatically fetched
    Field[] fields = ArrayUtils.addAll(newEntity.getClass().getDeclaredFields(),
            BaseEntity.class.getDeclaredFields());
    Object oldField = null;
    Object newField = null;
    StringBuilder delta = new StringBuilder();
    for (Field field : fields) {
        field.setAccessible(true); // We need to access private fields
        try {
            oldField = field.get(oldEntity);
            newField = field.get(newEntity);
        } catch (IllegalArgumentException e) {
            LOG.error("Bad argument given");
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            LOG.error("Could not access the argument");
            e.printStackTrace();
        }
        if ((oldField != newField)
                && (((oldField != null) && !oldField.equals(newField)) || ((newField != null) && !newField
                        .equals(oldField)))) {
            delta.append(field.getName()).append(": [").append(oldField).append("] -> [")
                    .append(newField).append("]  ");
        }
    }

    // Persist the difference
    if (delta.length() == 0) {
        LOG.warn("The delta is empty - this should not happen");
    } else {
        DeltaEntity deltaEntity = new DeltaEntity(oldEntity.getClass().toString(),
                oldEntity.getId(), oldEntity.getUuid(), email, delta.toString());
        mongoDataStore.save(deltaEntity);
    }
    return;
}

我们的 delta 实体看起来像这样(没有 getter + setter、toString、hashCode 和 equals):

Our delta entity looks like that (without the getters + setters, toString, hashCode, and equals):

@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;
    }

希望这可以帮助您入门:-)

Hope this helps you getting started :-)

相关文章