Đây là cách tôi đã kết thúc triển khai phiên bản cho các thực thể MongoDB. Nhờ cộng đồng StackOverflow giúp đỡ!
- Nhật ký thay đổi được lưu giữ cho từng thực thể trong bộ sưu tập lịch sử riêng biệt.
- Để tránh lưu nhiều dữ liệu, bộ sưu tập lịch sử không lưu trữ các phiên bản hoàn chỉnh, nhưng chỉ có phiên bản đầu tiên và sự khác biệt giữa các phiên bản. (Bạn thậm chí có thể bỏ qua phiên bản đầu tiên và xây dựng lại các phiên bản "ngược" từ phiên bản hiện tại trong bộ sưu tập chính của thực thể.)
- Java Object Diff được sử dụng để tạo các khác biệt đối tượng.
- Để có thể làm việc với các bộ sưu tập chính xác, cần thực hiện phương thức
equals
của các thực thể để nó kiểm tra khóa chính cơ sở dữ liệu chứ không phải thuộc tính phụ. (Nếu không, JavaObjectDiff sẽ không nhận ra các thay đổi về tài sản trong các phần tử thu thập.)
Đây là các thực thể tôi sử dụng để phiên bản (getters/setters, v.v.loại bỏ):
// 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;
}
Đây là hoạt động saveChangeHistory:
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()));
}
Đây là cách nó trông giống như trong MongoDB:
{
"_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[[email protected]]",
"state" : "ADDED",
"modified" : {
"_class" : "LetterSection",
"_id" : ObjectId("5034c7a83c75c52dddcbd556"),
"letterId" : "5034c7a83c75c52dddcbd554",
"sectionIndex" : 2,
"stringContent" : "BLABLA",
"contentMimetype" : "text/plain",
"sectionConfiguration" : "BLUBB"
}
}, {
"path" : "/sections[[email protected]]",
"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[[email protected]]/stringContent",
"state" : "CHANGED",
"base" : "blub5",
"modified" : "blub6"
}]
},
}],
"deleted" : false
}
EDIT: Đây là mã của khách:
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);
}
}
}
Không đầy đủ phiên bản, nhưng chúng tôi đã thực hiện một hệ thống kiểm toán nhỏ - khai thác gỗ đã thay đổi mà giá trị cũ sang những cái mới. Chúng tôi đang sử dụng phương pháp '' prePersist() '' của Morphia (sẽ chỉ làm việc để tiết kiệm thực thể đầy đủ, chứ không phải các bản cập nhật cụ thể). Có thể cung cấp một số mẫu mã, nhưng nó không có gì phức tạp ... – xeraa
Cảm ơn bạn đã bình luận! Tôi sẽ rất quan tâm đến một số chi tiết khác thể hiện giải pháp của bạn. Chỉ theo dõi việc lưu thực thể đầy đủ là hoàn toàn ok: Đây cũng là trường hợp sử dụng chính của chúng tôi. Một điểm rất thú vị là cách bạn so sánh tuổi với thực thể mới, xác định các thuộc tính đã thay đổi. Tôi đã xem xét các khung so sánh đồ thị ở đây, nhưng không tìm thấy giải pháp nhanh chóng và dễ dàng. –