كيف رفضت db4o في النظام الصناعي

صورة


نحن قسم شركة كبير تطوير نظام Java SE / MS SQL / db4o مهم. لعدة سنوات ، تحول المشروع من النموذج الأولي إلى التشغيل الصناعي وتحولت db4o إلى فرامل حسابية ، أردت التبديل من db4o إلى تقنية noSQL الحديثة. لقد تم التخلي عن التجربة والخطأ بعيدًا عن الخطة الأصلية - تم إلغاء db4o بنجاح ، ولكن على حساب تسوية. تحت انعكاسات القط وتفاصيل التنفيذ.


هل تقنية db4o ميتة؟


من الممكن العثور على Habré على عدد كبير من المنشورات حول db4o. في Stackoverflow ، يشبه نوع من النشاط المتبقي تعليقًا جديدًا على سؤال قديم أو سؤال جديد لم تتم الإجابة عليه . يعتقد ويكي بشكل عام أن الإصدار الحالي الثابت مؤرخ في عام 2011.


هذا يشكل انطباعًا عامًا: أن التكنولوجيا غير مهمة. كان هناك تأكيد رسمي : قررت شركة Actian عدم متابعة عرض منتج db4o التجاري بنشاط والعملاء من أجله.


كيف يتم حساب db4o


تتحدث المقالة " مقدمة إلى قواعد بيانات موجهة نحو الكائنات" عن الميزة الرئيسية لـ db4o - الغياب التام لنظام بيانات. يمكنك إنشاء أي كائن


User user1 = new User("Vasya", "123456", 25); 

ثم فقط اكتبه إلى ملف قاعدة البيانات


 db.Store(user1) 

يمكن بعد ذلك استرجاع الكائن المسجل باستخدام طريقة Query.execute () في النموذج الذي تم حفظه به.


في بداية المشروع ، أتاح ذلك ضمان سرعة عرض مسار التدقيق مع جميع البيانات المقدمة ، دون عناء مع هيكل الجداول العلائقية. هذا ساعد المشروع على البقاء على قيد الحياة. ثم كان هناك القليل من الموارد في الصندوق الرمل ، وبعد نهاية حساب اليوم مباشرة ، بدأت بيانات الغد يتم تحميلها في MS SQL. كان كل شيء يتغير باستمرار - تعرف على ما تم تقديمه تلقائيًا في الليل. ويمكن الوصول إلى ملف db4o
عند تصحيح الأخطاء ، قم باستخراج لقطة من اليوم المطلوب والإجابة على السؤال "لقد أرسلنا جميع البيانات ، لكنك لم تطلب أي شيء."


مع مرور الوقت ، اختفت مشكلة البقاء على قيد الحياة ، المشروع أقلعت ، وتغير العمل مع طلبات المستخدمين. فتح ملف db4o في تصحيح الأخطاء وتحليل سؤال صعب يمكن للمطور الذي هو دائما مشغول. بدلاً من ذلك ، هناك حشد من المحللين ، مُسلحين بوصف منطق الطلب وقادرون على استخدام الجزء المرئي للمستخدم فقط من البيانات. قريباً بدأ استخدام db4o فقط لعرض محفوظات الحساب. تماما مثل باريتو - جزء صغير من القدرات يوفر الحمل الرئيسي.


في العمليات القتالية ، يستغرق ملف السجل حوالي 35 جيجابايت / يوم ، ويستغرق التفريغ حوالي ساعة. يضغط الملف نفسه جيدًا (1:10) ، لكن لسبب ما لا تقوم مكتبة com.db4o.ObjectContainer بالضغط. في شمال CentOS ، تقوم مكتبة com.db4o.query.Query بكتابة / قراءة ملف حصريًا إلى دفق واحد. السرعة هي عنق الزجاجة.


رسم تخطيطي للجهاز


نموذج معلومات النظام هو التسلسل الهرمي للكائنات A و B و C و D. التسلسل الهرمي ليس شجرة ؛ الارتباطات C1 -> B1 مطلوبة للتشغيل.


 ROOT || | ==>A1 | || | | ==> B1 <------ | | || | | | | ======> C1 | | | | | | | ===> C1.$D | | =======> C2 | | | | ==> B2 ==> C2.$D | | ===>A2 =======> C3 | | ==> B3 ===> C3.$D | ======> C4 | ===> C4.$D 

يتفاعل المستخدم مع الخادم من خلال واجهة المستخدم (GUI) ، والتي يتم توفيرها من خلال com.sun.net.httpserver.HttpsServer ، العميل وخادم تبادل مستندات XML. في الشاشة الأولى ، يقوم الخادم بتعيين معرف لمستوى المستخدم ، والذي لا يتغير أكثر. إذا كان المستخدم يحتاج إلى سجل من مستوى ما ، يرسل واجهة المستخدم الرسومية إلى الخادم معرف ملفوف بتنسيق XML. يحدد الخادم قيم مفاتيح البحث في قاعدة البيانات ، ويفحص ملف db4o لليوم المطلوب ويسترجع الكائن المطلوب في الذاكرة بالإضافة إلى جميع الكائنات التي يشير إليها. إنشاء عرض تقديمي XML للمستوى المستخلص وإعادته إلى العميل.


عند المسح الضوئي لملف ما ، يقوم db40 افتراضيًا بقراءة جميع الكائنات التابعة إلى عمق معين ، مع استخلاص تسلسل هرمي كبير إلى حد ما مع الكائن المطلوب. يمكن تقليل وقت القراءة عن طريق تعيين الحد الأدنى لعمق التنشيط لفئة Foo غير الضرورية باستخدام conf.common (). ObjectClass (Foo.class) .maximumActivationDepth (1).


يؤدي استخدام فصول مجهولة المصدر إلى إنشاء مراجع ضمنية للفئة المرفقة بقيمة $ 0 . يعالج Db4o هذه الروابط ويستعيدها بشكل صحيح (لكن ببطء).


0. الفكرة


لذا ، يكون للمسؤولين تعبير غريب على وجوههم عندما يتعلق الأمر بدعم أو إدارة db4o. استخراج البيانات بطيء ، والتكنولوجيا ليست حية للغاية. المهمة: بدلاً من db4o ، قم بتطبيق تقنية NoSQL الحالية. زوج من بيانات الربيع + MongoDB لفت انتباهي.


1. النهج الأمامي


كانت فكرتي الأولى هي استخدام org.springframework.data.mongodb.core.MongoOperations وطريقة save () ، لأنها تبدو مثل com.db4o.ObjectContainer.db.Store (user1). تشير وثائق MongoDB إلى أن المستندات مخزنة في مجموعات ، فمن المنطقي تقديم كائنات النظام الضرورية كمستندات للمجموعات المقابلة. هناك أيضًا تعليقات توضيحية @ DBRef تتيح لك تنفيذ العلاقات بين المستندات بشكل عام وفقًا لروح 3NF . دعنا نذهب.


1.1. التفريغ. نوع المرجع الرئيسي


يتكون النظام من فئات POJO المصممة منذ فترة طويلة ودون مراعاة جميع هذه التقنيات الجديدة. يتم استخدام حقول نوع الخريطة <POJO ، POJO> ، وهناك منطق متفرع من العمل معهم. أنا أحفظ هذا الحقل ، وأحصل على خطأ


 org.springframework.data.mapping.MappingException: Cannot use a complex object as a key value. 

في هذه المناسبة ، تم العثور على المراسلات فقط لعام 2011 ، والتي تم فيها اقتراح تطوير MappingMongoConverter غير قياسي. لاحظت حتى الآن الحقول المشكلة @ عابرة ، أنا ذاهب. اتضح لحفظ ، ودراسة النتيجة.


يحدث الحفظ في المجموعة ، يتزامن اسمها مع اسم الفئة المحفوظة. لم أستخدم التعليقات التوضيحيةDBRef بعد ، لذلك لا يوجد سوى مجموعة واحدة ، وثائق JSON كبيرة جدًا ومتفرعة. لاحظت أنه عندما تقوم بحفظ الكائن ، فإن MongoOperations يمر بجميع الروابط غير الفارغة (بما في ذلك الوراثة) ويكتبها كمستند مرفق.


1.2. التفريغ. حقل مسمى أو صفيف؟


طراز النظام بحيث تحتوي الفئة C على إشارة إلى نفس الفئة D عدة مرات. في حقل defaultMode منفصل وبين الروابط الأخرى في ArrayList ، شيء من هذا القبيل

 public class C { private D defaultMode; private List<D> listOfD = new ArrayList<D>(); public class D { .. } public C(){ this.defaultMode = new D(); listOfD.add(defaultMode); } } 

بعد التفريغ ، ستحتوي وثيقة JSON على نسختين منها: وثيقة مرفقة باسم defaultMode وعنصر غير مسمى في مصفوفة الوثيقة. في الحالة الأولى ، يمكن الوصول إلى المستند بالاسم ، في الحالة الثانية - باسم المصفوفة مع فهرس. يمكنك البحث في مجموعات MongoDB في كلتا الحالتين. العمل فقط مع Spring Data و MongoDB ، توصلت إلى استنتاج أنه يمكنك استخدام ArrayList ، إذا كان بعناية ؛ لم ألاحظ أي قيود على استخدام المصفوفات. ظهرت الميزات لاحقًا ، على مستوى رابط MongoDB لـ BI.


1.3. التحميل. حجج المنشئ


أحاول قراءة مستند محفوظ باستخدام طريقة MongoOperations.findOne (). تحميل كائن A من قاعدة البيانات يلقي استثناء


 "No property name found on entity class A to bind constructor parameter to!" 

اتضح أن الفئة تحتوي على حقل اسم corpName وأن المُنشئ لديه معلمة اسم سلسلة ، وهذا .corpName = الاسم يتم تعيينه في نص المنشئ. تتطلب MongoOperations أن تتطابق أسماء الحقول في الفئات مع أسماء وسيطات المُنشئ. إذا كان هناك العديد من المنشئات ، فأنت بحاجة إلى تحديد واحد مع تعليق توضيحي علىPersistenceConstructor. أحمل أسماء الحقول والمعلمات في المراسلات.


1.4. التحميل. مع $ D وهذا 0 دولار


تتضمن الفئة D الداخلية المتداخلة السلوك الافتراضي للفئة C ولا معنى لها بشكل منفصل عن الفئة C. يتم إنشاء مثيل D لكل مثيل C والعكس - لكل مثيل D يوجد مثيل C الذي تم إنشاؤه ، ولا يزال لدى الفئة D أحفاد ينفذون سلوكيات بديلة ويمكن تخزينهم في listOfD. يتطلب مُنشئ الفئات المنحدرة من D وجود كائن موجود بالفعل C.


بالإضافة إلى الطبقات الداخلية المتداخلة ، يستخدم النظام الطبقات الداخلية المجهولة . كما تعلمون ، يحتوي كلاهما على مرجع ضمني لمثيل لفئة مرفقة. أي كجزء من كل مثيل لكائن القرص المضغوط ، يقوم المترجم بإنشاء رابط هذا $ 0 ، والذي يشير إلى الكائن الأصل C.


مرة أخرى ، أحاول قراءة المستند المحفوظ من المجموعة والحصول على استثناء


 "No property this$0 found on entity class $D to bind constructor parameter to!" 

أذكر أن أساليب الفئة D تستخدم القوة الكاملة لمراجع C.this.fieldOfClassC ، وأن أحفاد الفئة D يتطلبون منشئ إنشاء C كحجة. أي ، أنا بحاجة إلى توفير ترتيب معين لإنشاء كائنات في MongoOperations بحيث يمكن تحديد الكائن الأصل C في مُنشئ D. مرة أخرى ، MappingMongoConverter غير القياسي؟


ربما لا تستخدم الطبقات مجهولة المصدر وجعل الطبقات الداخلية طبيعية؟ تعتبر عملية تحسين أو تحسين بنية النظام المطبق بالفعل مهمة مبهرة ...


2. النهج من 3NF / @ DBRef


أحاول الانتقال من ناحية أخرى ، وحفظ كل فصل في مجموعتي وإجراء اتصالات بينهما بروح 3NF.


2.1. التفريغ. DBRef جميلة


تحتوي الفئة C على العديد من المراجع إلى D. إذا تم وضع علامة على ارتباطي defaultMode و ArrayList على أنهDBRef ، فسيقل حجم المستند ، بدلاً من المستندات المرفقة الضخمة ، سيكون هناك ارتباطات أنيقة. في الحقل json ، تظهر وثيقة المجموعة C

 "defaultMode" : DBRef("D", ObjectId("5c496eed2c9c212614bb8176")) 

في قاعدة بيانات MongoDB ، يتم إنشاء مجموعة D تلقائيًا ووثيقة تحتوي على حقل فيها


 "_id" : ObjectId("5c496eed2c9c212614bb8176") 

كل شيء بسيط وجميل.


2.2. التحميل. منشئ الفئة د


عند العمل مع الارتباطات ، يعرف الكائن C أنه يتم إنشاء الكائن D الافتراضي مرة واحدة بالضبط. إذا كنت بحاجة إلى تجاوز جميع الكائنات D باستثناء الكائن الافتراضي ، فما عليك سوى مقارنة الروابط:


 private D defaultMode; private ArrayList<D> listOfD; for (D currentD: listOfD){ if (currentD == defaultMode) continue; doSomething(currentD); } 

أدعو findOne () ، أدرس الفصل الدراسي C. اتضح أن MongoOperations يقرأ مستند json ويدعو مُنشئ D لكل تعليق توضيحيDBRef يواجهه ، في كل مرة يقوم بإنشاء كائن جديد. أحصل على بنية غريبة - مرجعان مختلفان إلى D في حقل defaultMode وفي صفيف listOfD ، حيث يجب أن يكون الرابط هو نفسه.


التعلم من المجتمع : "يجب تجنب Dbref في رأيي عند العمل مع mongodb." هناك اعتبار آخر في السياق نفسه من الوثائق الرسمية: نموذج البيانات غير الطبيعي حيث يتم تخزين البيانات ذات الصلة ضمن وثيقة واحدة سيكون الأمثل لحل DBRefs ، يجب أن يقوم التطبيق الخاص بك بإجراء استعلامات إضافية لإرجاع الوثائق المرجعية.


تقول صفحة الوثائق المذكورة في البداية: "بالنسبة للعديد من حالات الاستخدام في MongoDB ، سيكون نموذج البيانات غير الطبيعي حيث يتم تخزين البيانات ذات الصلة ضمن وثيقة واحدة هو الأمثل." هل هو مكتوب لي؟


يشير التركيز مع المصمم إلى أنك لست بحاجة إلى التفكير في نظام قواعد البيانات الترابطية. الخيار هو:


  • إذا حددتDBRef:
    • سيتم استدعاء مُنشئ كل تعليق توضيحي وسيتم إنشاء العديد من الكائنات المتطابقة ؛
    • سوف تجد MongoOperations جميع المستندات من جميع المجموعات ذات الصلة وقراءتها. سيكون هناك طلب للفهرس بواسطة ObjectId ثم القراءة من مجموعات كثيرة من قاعدة بيانات (كبيرة) ؛
  • إذا لم تحدد ، فسيتم حفظ ملف json "غير الطبيعي" بتكرار نفس البيانات.

ألاحظ بنفسي: لا يمكنك الاعتماد علىDBRef ، ولكن استخدام حقل نوع ObjectId ، وملؤه يدويًا. في هذه الحالة ، بدلا من


 "defaultMode" : DBRef("D", ObjectId("5c496eed2c9c212614bb8176")) 

سوف تحتوي على وثيقة json


 "defaultMode" : ObjectId("5c496eed2c9c212614bb8176") 

لن يكون هناك تحميل تلقائي - لا تعرف MongoOperations المجموعة التي تبحث عن مستند. يجب تحميل المستند في طلب (كسول) منفصل يشير إلى المجموعة و ObjectId. يجب أن يُرجع استعلام واحد النتيجة بسرعة ، بالإضافة إلى ذلك ، يتم إنشاء فهرس تلقائي لكل مجموعة بواسطة ObjectId.


2.3. ماذا الان؟


المجاميع الفرعية. لم يكن من الممكن تطبيق وظيفة db4o بسرعة وسهولة على MongoDB:


  • من غير الواضح كيفية استخدام POJO مخصص كمفتاح قائمة مفتاح - قيمة ؛
  • من غير الواضح كيفية تعيين ترتيب إنشاء كائنات في MappingMongoConverter
  • من غير الواضح ما إذا كنت تريد تحميل مستند "غير طبيعي" بدون DBRef وما إذا كان من الضروري الخروج بآليته الخاصة للتهيئة البطيئة.

يمكنك إضافة التحميل كسول. يمكنك محاولة القيام MappingMongoConverter. يمكنك تعديل المنشئات / الحقول / القوائم الحالية. ولكن هناك سنوات عديدة من منطق العمل - وليس تغييرًا ضعيفًا وخطر عدم اختباره أبدًا.


حل وسط: لإنشاء آلية جديدة لحفظ البيانات للمشكلة التي يجري حلها ، مع الاحتفاظ بآلية التفاعل مع واجهة المستخدم الرسومية.


3. المحاولة الثالثة ، تجربة الأولين


يقترح باريتو أن حل المشكلات مع سرعة المستخدمين سيعني نجاح المهمة بأكملها. المهمة هي: تحتاج إلى معرفة كيفية حفظ واستعادة بيانات العرض التقديمي للمستخدم بسرعة دون db4o.


سيفقد هذا القدرة على فحص الكائن المحفوظ في تصحيح الأخطاء. من ناحية ، هذا أمر سيء. من ناحية أخرى ، نادراً ما تحدث مثل هذه المهام ، ويتم وضع علامات على جميع عمليات التسليم القتالية. من أجل التسامح مع الخطأ ، في كل مرة قبل التفريغ ، يقوم النظام بإجراء تسلسل الحساب إلى ملف. إذا كنت بحاجة إلى فحص كائن في تصحيح الأخطاء ، فيمكنك إجراء التسلسل واستنساخ تجميع النظام المقابل واستعادة الحساب.


3.1. بيانات العرض التقديمي المخصص


لإنشاء عروض تقديمية لمستويات المستخدمين ، يحتوي النظام على فئة عارض خاصة. تتلقى طريقة Viewer.getXML () مستوى كمدخلات ، وتستخرج القيم الرقمية وسلسلة القيم اللازمة منها ، وتقوم بإنشاء XML.


إذا طلب المستخدم إظهار مستوى حساب اليوم ، فسيتم العثور على المستوى في ذاكرة الوصول العشوائي. لإظهار حساب من الماضي ، ستجد طريقة com.db4o.query.Query.execute () المستوى في الملف. لا يختلف المستوى من الملف تقريبًا عن المستوى الذي تم إنشاؤه للتو ، وسيقوم Viewer بإنشاء العرض التقديمي دون ملاحظة الاستبدال.


لحل مشكلتي ، أحتاج إلى وسيط بين مستوى الحساب والعرض التقديمي الخاص به - إطار العرض التقديمي (الإطار) ، الذي سيقوم بتخزين البيانات والبناء على بيانات XML المتاحة. ستصبح سلسلة الإجراءات لبناء العرض التقديمي أطول ، وفي كل مرة يتم إنشاء إطار وإنشاء الإطار XML:


  : < > -> Viewer.getXML() : < > -> Viewer.getFrame() -> Frame.getXML() 

عند حفظ المجموعة النصية ، ستحتاج إلى إنشاء إطارات من جميع المستويات والكتابة إلى قاعدة البيانات.


3.2. التفريغ


كانت المهمة بسيطة نسبيا ولم تكن هناك مشاكل معها. كرر هيكل العرض التقديمي XML ، تلقى الإطار جهاز عودي في شكل تسلسل هرمي من العناصر مع الحقول سلسلة ، عدد صحيح ومزدوج. يطلب الإطار getXML () من جميع عناصره ، ويجمعه في وثيقة واحدة ، ويعود. قام MongoOperations بعمل رائع مع الطبيعة العودية للإطار ولم يطرح أسئلة جديدة أثناء تقدمه.


وأخيرا ، كل شيء أقلعت! يضغط محرك WiredTiger افتراضيًا على مجموعات مستندات MongoDB ؛ على نظام الملفات ، استغرق التفريغ حوالي 3.5 جيجابايت في اليوم. انخفاض عشرة أضعاف على db4o ليست سيئة.


في البداية ، تم ترتيب عملية التفريغ ببساطة - اجتياز متكرر للشجرة المستوية ، MongoOperations.save () لكل منها. استغرق هذا التفريغ 5.5 ساعة ، وهذا على الرغم من حقيقة أن بناء العروض لا يتضمن سوى قراءة الأشياء. أقوم بإضافة multithreading: اجتياز شجرة المستوى بشكل متكرر ، وتقسيم جميع المستويات المتاحة إلى حزم بحجم معين ، وإنشاء تطبيقات Callable.call () وفقًا لعدد الحزم ، ونقل كل حزمة إلى الحزمة الخاصة بنا والقيام بكل ذلك من خلال ExecutorService.invokeAll ().


لم تطرح MongoOperations مرة أخرى أي أسئلة وأدت مهمة رائعة باستخدام وضع متعدد الخيوط. تم تحديد حجم الحزمة تجريبياً ، مما يوفر أفضل سرعة تفريغ. اتضح 15 دقيقة لمجموعة من 1000 المستويات.


3.3. Mongo BI Connector ، أو كيف يعمل الناس معها


لغة الاستعلام MongoDB كبيرة وقوية ، لقد اكتسبت حتماً خبرة في العمل معها ، وصلت إلى هذا المكان. تدعم وحدة التحكم JavaScript ، يمكنك كتابة تصميمات جميلة وقوية. هذا جانب واحد. من ناحية أخرى ، يمكنني كسر أدمغة نصف جيدة من الزملاء المحللين مع طلب


 db.users.find( { numbers: { $in: [ 390, 754, 454 ] } } ); 

بدلا من المعتاد


 SELECT * FROM users WHERE numbers IN (390, 754, 454) 

يأتي MongoDB Connector for BI في عملية الإنقاذ ، والتي يمكنك من خلالها تقديم مستندات المجموعة في شكل جدول. تسمى قاعدة بيانات MongoDB قاعدة بيانات تستند إلى المستندات ، ولا تعرف كيفية تقديم تسلسل هرمي من الحقول / المستندات في شكل جدول. لكي يعمل الموصل ، من الضروري وصف بنية الجدول المستقبلي في ملف .drdl منفصل ، يشبه تنسيقه yaml جدًا. في الملف ، يجب عليك تحديد المراسلات بين حقل الجدول العلائقي في الإخراج والمسار إلى حقل مستند JSON عند الإدخال.


3.4. ميزات باستخدام المصفوفات


قيل أعلاه أنه بالنسبة لـ MongoDB نفسه ، لا يوجد فرق خاص بين مجموعة وحقل. من منظور الموصل ، يختلف الصفيف اختلافًا كبيرًا عن الحقل المحدد ؛ حتى اضطررت إلى refactor فئة الإطار النهائي. يجب استخدام مجموعة من المستندات فقط عندما يكون ذلك ضروريًا لوضع جزء من المعلومات في جدول مرتبط.


إذا كانت وثيقة JSON عبارة عن تسلسل هرمي للحقول المسماة ، فيمكن الوصول إلى أي حقل عن طريق تحديد المسار من جذر المستند خلال فترة ، على سبيل المثال xy. إذا تم تحديد المراسلات xy => fieldXY في ملف DRDL ، فسيحتوي جدول الإخراج على عدد الصفوف الموجودة في المجموعة عند المدخل. إذا لم يكن هناك حقل xy في بعض المستندات ، فسيكون NULL في الصف المقابل من الجدول.


افترض أن لدينا قاعدة بيانات MongoDB تدعى Frames ، هناك مجموعة A في قاعدة البيانات ، وسجلت MongoOperations حالتين من الفئة A في هذه المجموعة.


 { "_id": ObjectId("5cdd51e2394faf88a01bd456"), "x": { "y": "xy string value 1"}, "days": [{ "k": "0", "v": 0.0 }, { "k": "1", "v": 0.1 }], "_class": "A" } 

والثاني (ObjectId يختلف حسب الرقم الأخير):


 { "_id": ObjectId("5cdd51e2394faf88a01bd457"), "x": { "y": "xy string value 2"}, "days": [{ "k": "0", "v": 0.3 }, { "k": "1", "v": 0.4 }], "_class": "A" } 

لا يستطيع موصل BI الوصول إلى عناصر الصفيف حسب الفهرس ، ولا يمكن استخراج ، على سبيل المثال ، حقل الأيام [1] .v من صفيف في جدول. بدلاً من ذلك ، يمكن للموصل تمثيل كل عنصر من صفيف الأيام كصف في جدول منفصل باستخدام عامل التشغيل $ الاسترخاء . سيتم ربط هذا الجدول المنفصل بالعلاقة الأصلية بين أطراف من خلال معرف الصف. في مثالنا ، يتم تعريف الجداول tableA لوثائق المجموعة و tableA_days لمستندات صفيف الأيام. يبدو الملف .drdl كما يلي:


 schema: - db: Frames tables: - table: tableA collection: A pipeline: [] columns: - Name: _id MongoType: bson.ObjectId SqlName: _id SqlType: objectid - Name: xy MongoType: string SqlName: fieldXY SqlType: varchar - table: tableA_days collection: A pipeline: - $unwind: path: $days columns: - Name: _id #   MongoType: bson.ObjectId SqlName: tableA_id SqlType: objectid - Name: days.k MongoType: string SqlName: tableA_dayNo SqlType: varchar - Name: days.v MongoType: string SqlName: tableA_dayVal SqlType: varchar 

محتويات الجداول ستكون: table tableA


_IDfieldXY
5cdd51e2394faf88a01bd456قيمة سلسلة س ص 1
5cdd51e2394faf88a01bd457قيمة سلسلة س ص 2

والجدول الجدولأيام


tableA_idtableA_dayNotableA_dayVal
5cdd51e2394faf88a01bd45600.0
5cdd51e2394faf88a01bd45610.1
5cdd51e2394faf88a01bd45700.3
5cdd51e2394faf88a01bd45710.4

في المجموع


لم يكن من الممكن تنفيذ المهمة في الصيغة الأصلية ؛ لا يمكنك فقط اتخاذ واستبدال db4o بـ MongoDB. لا تستطيع MongoOperations استعادة أي كائن تلقائيًا مثل db4o. ربما يمكنك القيام بذلك ، لكن تكاليف العمالة لن تكون قابلة للمقارنة لاستدعاء أساليب المتجر / الاستعلام في مكتبة db4o.


درب التدقيق. Db4o هي أداة مفيدة للغاية في بداية المشروع. يمكنك ببساطة كتابة الكائن ، ثم استعادته وفي الوقت نفسه لا تقلق وجداول. كل هذا مع تحذير مهم: إذا كنت بحاجة إلى تغيير التسلسل الهرمي للفئات (إضافة الفئة E بين A و B) ، فإن كل المعلومات المخزنة مسبقًا تصبح غير قابلة للقراءة. لكن لبدء هذا المشروع ، فهذا ليس مهمًا للغاية ، طالما لا توجد مجموعة كبيرة من الملفات القديمة المتراكمة.


عندما كانت هناك خبرة كافية مع MongoOperations ، لم تتسبب كتابة التحميل في حدوث مشكلات. إن كتابة رمز جديد للإطار أسهل بكثير من إعادة كتابة الكود القديم ، الذي يتم إنتاجه أيضًا.

Source: https://habr.com/ru/post/ar461417/


All Articles