2012-08-24 11 views
11

मुझे दस्तावेज़-उन्मुख डेटाबेस (मोंगोडीबी) में संग्रहीत (ऑब्जेक्ट) जावा ऑब्जेक्ट ग्राफ़ पर वर्जनिंग करने की आवश्यकता है। संबंधपरक डेटाबेस और हाइबरनेट के लिए, मैंने Envers की खोज की और संभावनाओं के बारे में बहुत आश्चर्यचकित हूं। क्या ऐसा कुछ ऐसा है जिसका उपयोग स्प्रिंग डेटा दस्तावेज़ों के साथ किया जा सकता है?जावा मोंगोडीबी ऑब्जेक्ट वर्जनिंग

मुझे this post ऑब्जेक्ट संस्करणों को संग्रहीत करने के बारे में विचारों (और अधिक ...) को रेखांकित करने के लिए मिला, और मेरा वर्तमान कार्यान्वयन इसी तरह काम करता है कि यह ऑब्जेक्ट की प्रतियों को एक टाइमस्टैम्प के साथ एक अलग इतिहास संग्रह में संग्रहीत करता है, लेकिन मैं स्टोरेज स्पेस को बचाने के लिए इसे बेहतर बनाना चाहते हैं। इसलिए, मुझे लगता है कि मुझे ऑब्जेक्ट पेड़ पर "diff" ऑपरेशन और पुराने ऑब्जेक्ट्स के पुनर्निर्माण के लिए "विलय" ऑपरेशन दोनों को लागू करने की आवश्यकता है। क्या इसमें कोई पुस्तकालय हैं जो इससे मदद कर रहे हैं?

संपादित करें: मोंगोडीबी और संस्करण के साथ किसी भी अनुभव की अत्यधिक सराहना की! मुझे लगता है कि शायद स्प्रिंग डेटा समाधान नहीं होगा।

+0

नहीं पूर्ण संस्करण के द्वारा समर्थित है, लेकिन हम एक छोटे से लेखा परीक्षा प्रणाली को क्रियान्वित किया है - प्रवेश जो जो पुराने मूल्यों को बदल दिया नए लोगों के लिए। हम मॉर्फिया की 'प्रीपेर्सिस्ट()' विधि का उपयोग कर रहे हैं (जो केवल पूर्ण इकाई के लिए काम करेगा, विशिष्ट अपडेट नहीं)। कुछ कोड नमूने प्रदान कर सकते हैं, लेकिन यह परिष्कृत कुछ भी नहीं है ... – xeraa

+0

आपकी टिप्पणी के लिए धन्यवाद! मुझे आपके समाधान का प्रदर्शन करने वाले कुछ और विवरणों में बहुत दिलचस्पी होगी। केवल पूर्ण इकाई बचत को ट्रैक करना निश्चित रूप से ठीक है: यह हमारा मुख्य उपयोग केस भी है। एक बहुत ही रोचक बिंदु यह है कि जिस तरह से आप पुराने इकाई से तुलना करते हैं, बदले गए गुणों की पहचान करते हैं। मैंने ग्राफ तुलना ढांचे में एक नज़र डाली, लेकिन एक त्वरित और आसान समाधान नहीं मिला। –

उत्तर

7

हम एक मूल इकाई का उपयोग कर रहे हैं (जहां हमने आईडी सेट किया है, सृजन + अंतिम परिवर्तन तिथियां ...)। इस पर निर्माण हम एक सामान्य हठ विधि है, जो कुछ इस तरह दिखता का उपयोग कर रहे:

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

डेल्टा विधि (मैं संभव के रूप में सामान्य रूप में इस बनाने के लिए कोशिश करता हूँ) इस तरह दिखता है:

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

हमारे डेल्टा इकाई (getters + setters, toString, hashCode बिना, और बराबर होती है) कि तरह लग रहा है:

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

आशा इस मदद करता है :-) शुरू हो रही

+0

नमूना के लिए बहुत बहुत धन्यवाद। मुझे जावा ऑब्जेक्ट diffs के बारे में एक पोस्ट भी मिली (http://stackoverflow.com/questions/8001400/is-there-a-java-library-that-can-diff-two-objects) इस लाइब्रेरी का उल्लेख करते हुए: https: // github.com/SQiShER/java-object-diff - शायद मैं इस diff एल्गोरिदम के साथ अपने समाधान "मसाला" कर सकते हैं। मैं इस प्रश्न को कुछ और समय के लिए खोलना चाहता हूं, शायद अन्य विचार भी हैं। –

+0

दिलचस्प परियोजना, आपके समाधान की प्रतीक्षा कर रही है। इस दौरान एक उपरोक्त अभी भी सराहना की जाएगी ;-) – xeraa

12

इस प्रकार मैंने मोंगोडीबी इकाइयों के लिए संस्करण को कार्यान्वित करने का अंत किया। मदद के लिए StackOverflow समुदाय के लिए धन्यवाद!

  • प्रत्येक इकाई के लिए एक अलग इतिहास संग्रह में एक परिवर्तन लॉग रखा जाता है।
  • बहुत सारे डेटा को सहेजने से बचने के लिए, इतिहास संग्रह पूर्ण उदाहरणों को संग्रहीत नहीं करता है, लेकिन संस्करणों के बीच केवल पहला संस्करण और अंतर। (आप पहले संस्करण को छोड़ सकते हैं और इकाई के मुख्य संग्रह में मौजूदा संस्करण से "पीछे की ओर" संस्करणों का पुनर्निर्माण कर सकते हैं।)
  • Java Object Diff ऑब्जेक्ट diffs उत्पन्न करने के लिए उपयोग किया जाता है।
  • आदेश सही ढंग से संग्रह के साथ काम करने में सक्षम होने के लिए, एक संस्थाओं की equals विधि लागू करने के लिए इतना है कि यह डेटाबेस प्राथमिक कुंजी और न उप संपत्तियों के लिए परीक्षण की जरूरत है। (अन्यथा, JavaObjectDiff संग्रह तत्वों में संपत्ति परिवर्तनों को पहचान नहीं पाएगा।)

यहां वे संस्थाएं हैं जिन्हें मैं संस्करण (गेटर्स/सेटर्स इत्यादि) के लिए उपयोग करता हूं।निकाला गया):

{ 
    "_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 
} 

संपादित करें:

: यहाँ आगंतुक कोड है

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())); 
} 

यह कि यह कैसा दिखता MongoDB में की तरह है:

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

यहाँ saveChangeHistory ऑपरेशन है

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

} 
2

दिखता है जैसे जैवर्स आर है इस काम के लिए ight उपकरण, देखने http://javers.org/documentation/features/#javers-repository

Javers धारणात्मक डोमेन वस्तु संस्करण के लिए एक VCS, JSON और MongoDB

संबंधित मुद्दे