Skip to content

2. البنية الطبقية لتطبيق جافا

غالبًا ما يتم تقسيم تطبيق Java إلى طبقات، لكل منها دور محدد بوضوح. لننظر إلى بنية شائعة، وهي البنية ثلاثية الطبقات:

  • الطبقة [1]، المشار إليها هنا بـ [ui] (واجهة المستخدم)، هي الطبقة التي تتفاعل مع المستخدم عبر واجهة المستخدم الرسومية Swing، أو واجهة وحدة التحكم، أو واجهة الويب. وتتمثل مهمتها في توفير البيانات من المستخدم إلى الطبقة [2] أو عرض البيانات التي توفرها الطبقة [2] للمستخدم.
  • الطبقة [2]، المشار إليها هنا بـ [business]، هي الطبقة التي تطبق ما يُسمى بقواعد العمل — أي المنطق الخاص بالتطبيق — دون الاهتمام بمصدر البيانات التي تتلقاها أو وجهة النتائج التي تنتجها.
  • الطبقة [3]، المشار إليها هنا بـ [DAO] (كائن الوصول إلى البيانات)، هي الطبقة التي تزود الطبقة [2] بالبيانات المخزنة مسبقًا (الملفات وقواعد البيانات وما إلى ذلك) وتخزن بعض النتائج التي توفرها الطبقة [2].

هناك طرق مختلفة لتنفيذ طبقة [DAO]. دعونا نستعرض بعضًا منها:

طبقة [JDBC] المذكورة أعلاه هي الطبقة القياسية المستخدمة في Java للوصول إلى قواعد البيانات. وهي تعزل طبقة [DAO] عن نظام إدارة قواعد البيانات (DBMS) الذي يدير قاعدة البيانات. نظريًا، يمكن للمرء تبديل أنظمة إدارة قواعد البيانات دون تغيير كود طبقة [DAO]. على الرغم من هذه الميزة، فإن واجهة برمجة تطبيقات JDBC (JDBC API) لها بعض العيوب:

  • من المرجح أن تؤدي جميع العمليات على نظام إدارة قواعد البيانات (DBMS) إلى إثارة استثناء SQLException. وهذا يجبر الكود المستدعي (طبقة [DAO] في هذه الحالة) على تغليفها في كتل try/catch، مما يجعل الكود مطولًا للغاية.
  • طبقة [DAO] ليست مستقلة تمامًا عن نظام إدارة قواعد البيانات (DBMS). على سبيل المثال، تمتلك أنظمة إدارة قواعد البيانات (DBMS) طرقًا خاصة بها لتوليد قيم المفاتيح الأساسية تلقائيًا، والتي لا يمكن لطبقة [DAO] تجاهلها. وبالتالي، عند إدراج سجل:
    • مع Oracle، يجب أن تحصل طبقة [DAO] أولاً على قيمة للمفتاح الأساسي للسجل ثم تقوم بإدراج السجل.
    • مع SQL Server، تقوم طبقة [DAO] بإدراج السجل، الذي يتم تعيين قيمة مفتاح أساسي له تلقائيًا بواسطة نظام إدارة قواعد البيانات (DBMS)، وهي قيمة يتم إرجاعها إلى طبقة [DAO].

يمكن التخلص من هذه الاختلافات باستخدام الإجراءات المخزنة. في المثال السابق، ستستدعي طبقة [DAO] إجراءً مخزّنًا في Oracle أو SQL Server يأخذ في الاعتبار الميزات المحددة لنظام إدارة قواعد البيانات. وستكون هذه الإجراءات مخفية عن طبقة [DAO]. ومع ذلك، في حين أن تغيير نظام إدارة قواعد البيانات لن يتطلب إعادة كتابة طبقة [DAO]، فإنه سيتطلب إعادة كتابة الإجراءات المخزنة. وقد لا يُعتبر هذا عائقًا كبيرًا.

بذلت جهود عديدة لعزل طبقة [DAO] عن الجوانب الخاصة بنظم إدارة قواعد البيانات. أحد الحلول التي حققت نجاحًا كبيرًا في هذا المجال في السنوات الأخيرة هو Hibernate:

تقع طبقة [Hibernate] بين طبقة [DAO] التي يكتبها المطور وطبقة [JDBC]. Hibernate هو أداة ORM (مُخطِط علاقات الكائنات)، وهي أداة تربط عالم قواعد البيانات العلائقية بعالم الكائنات التي تعالجها لغة Java. لم يعد مطور طبقة [DAO] يرى طبقة [JDBC] أو جداول قاعدة البيانات التي يرغب في استخدام محتواها. بل يرى فقط تمثيل الكائنات لقاعدة البيانات، الذي توفره طبقة [Hibernate]. يتم إنشاء الجسر بين جداول قاعدة البيانات والكائنات التي تتعامل معها طبقة [DAO] بشكل أساسي بطريقتين:

  • عبر ملفات التكوين بنمط XML
  • من خلال تعليقات Java في الكود، وهي تقنية متاحة فقط منذ JDK 1.5

طبقة [Hibernate] هي طبقة تجريدية مصممة لتكون شفافة قدر الإمكان. السيناريو المثالي هو ألا يدرك مطور طبقة [DAO] تمامًا أنه يعمل مع قاعدة بيانات. وهذا ممكن إذا لم يكن هو من يكتب التكوين الذي يربط بين عالمي العلاقات والكائنات. تكوين هذا الجسر أمر دقيق للغاية ويتطلب بعض الخبرة.

يُطلق على طبقة الكائنات [4]، التي تعكس قاعدة البيانات، اسم "سياق الاستمرارية". تقوم طبقة [DAO] القائمة على Hibernate بتنفيذ عمليات الاستمرارية (CRUD: إنشاء، قراءة، تحديث، حذف) على الكائنات في سياق الاستمرارية؛ ويقوم Hibernate بترجمة هذه العمليات إلى عبارات SQL تنفذها طبقة JDBC. بالنسبة لعمليات استعلام قاعدة البيانات (SQL SELECT)، يوفر Hibernate للمطورين لغة HQL (لغة استعلام Hibernate) لاستعلام سياق الاستمرارية [4] بدلاً من قاعدة البيانات نفسها.

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

في ضوء نجاح منتجات ORM، قررت شركة Sun، مبتكرة لغة Java، توحيد طبقة ORM عبر مواصفة تسمى JPA (Java Persistence API)، والتي تم إصدارها جنبًا إلى جنب مع Java 5. تم تطبيق مواصفة JPA في العديد من المنتجات: Hibernate، TopLink، EclipseLink، OpenJPA، إلخ. مع JPA، تصبح البنية السابقة كما يلي:

تتفاعل طبقة [DAO] الآن مع مواصفات JPA، وهي مجموعة من الواجهات. وقد استفاد المطورون من حيث التوحيد. في السابق، إذا قام مطور بتغيير طبقة ORM الخاصة به، كان عليه أيضًا تغيير طبقة [DAO] الخاصة به، والتي تمت كتابتها للتفاعل مع ORM محدد. الآن، سيقومون بكتابة طبقة [DAO] تتفاعل مع طبقة JPA. وبغض النظر عن المنتج الذي ينفذ طبقة JPA، تظل الواجهة المقدمة لطبقة [DAO] كما هي.

في هذا المستند، سنستخدم طبقة [DAO] مبنية فوق طبقة JPA/Hibernate أو JPA/EclipseLink. سنستخدم أيضًا إطار عمل Spring 2.8 لربط هذه الطبقات معًا.

الميزة الرئيسية لـ Spring هي أنه يتيح لك ربط الطبقات من خلال التكوين بدلاً من الكود. وبالتالي، إذا كان من الضروري استبدال تطبيق JPA/Hibernate بتطبيق Hibernate بدون JPA — على سبيل المثال، لأن التطبيق يعمل في بيئة JDK 1.4 لا تدعم JPA — فإن هذا التغيير في تطبيق طبقة [DAO] لا يؤثر على كود طبقة [الأعمال]. ما عليك سوى تعديل ملف تكوين Spring الذي يربط الطبقات ببعضها البعض.

مع Java EE 5، يوجد حل آخر: تنفيذ طبقات [الأعمال] و[DAO] باستخدام EJB3 (Enterprise JavaBeans الإصدار 3):

سنرى أن هذا الحل لا يختلف كثيرًا عن الحل الذي يستخدم Spring. تتوفر بيئة Java EE 5 ضمن ما يُسمى بخوادم التطبيقات مثل Sun Application Server 9.x (Glassfish) وJBoss Application Server وOracle Container for Java (OC4J) ... وخادم التطبيقات هو في الأساس خادم تطبيقات ويب. هناك أيضًا ما يُسمى ببيئات EE 5 "المستقلة"، أي تلك التي يمكن استخدامها خارج خادم التطبيقات. وهذا هو الحال مع JBoss EJB3 أو OpenEJB.

في بيئة EE5، يتم تنفيذ الطبقات بواسطة كائنات تسمى EJBs (Enterprise Java Beans). في الإصدارات السابقة من EE، كان من المعروف أن EJBs (EJB 2.x) صعبة التنفيذ والاختبار، وأحيانًا كان أداؤها أقل من المتوقع. يتم التمييز بين حبوب "الكيان" EJB 2.x وحبوب "الجلسة" EJB 2.x. باختصار، تتوافق "كيانات" EJB 2.x مع صف في جدول قاعدة البيانات، و"جلسات" EJB 2.x هي كائنات تُستخدم لتنفيذ طبقات [منطق الأعمال] و[DAO] في بنية متعددة الطبقات. أحد الانتقادات الرئيسية الموجهة إلى الطبقات التي يتم تنفيذها باستخدام EJBs هو أنها لا يمكن استخدامها إلا داخل حاويات EJB، وهي خدمة توفرها بيئة EE (Enterprise Edition). هذه البيئة، التي يعد إعدادها أكثر تعقيدًا من بيئة SE (Standard Edition)، قد تثني المطورين عن إجراء الاختبارات بشكل متكرر. ومع ذلك، هناك بيئات تطوير Java تسهل استخدام خادم التطبيقات من خلال أتمتة نشر EJBs على الخادم: Eclipse وNetBeans وJDeveloper وIntelliJ IDEA. هنا، سنستخدم NetBeans 6.8 وخادم التطبيقات GlassFish v3.

تم إنشاء إطار عمل Spring استجابةً لتعقيد EJB2. في بيئة SE، يوفر Spring عددًا كبيرًا من الخدمات التي تقدمها عادةً بيئات EE. على سبيل المثال، في قسم "استمرارية البيانات"، يوفر Spring مجموعات الاتصال ومديري المعاملات التي تتطلبها التطبيقات. وقد عزز ظهور Spring ثقافة اختبار الوحدات، التي أصبح تنفيذها أسهل في سياق SE مقارنةً بسياق EE. يسمح Spring بتنفيذ طبقات التطبيقات باستخدام كائنات Java القياسية (POJO، Plain Old/Ordinary Java Object)، مما يتيح إعادة استخدامها في سياقات أخرى. أخيرًا، يدمج العديد من أدوات الجهات الخارجية بشكل شفاف إلى حد ما، ولا سيما أدوات الاستمرارية مثل Hibernate و EclipseLink و iBatis، ...

تم تصميم Java EE 5 لمعالجة أوجه القصور في مواصفات EJB 2. تطورت EJB 2.x إلى EJB 3. هذه كائنات POJO مزودة بعلامات تجعلها كائنات خاصة عندما تكون داخل حاوية EJB 3. داخل الحاوية، يمكن لـ EJB3 الاستفادة من خدمات الحاوية (مجمع الاتصالات، مدير المعاملات، إلخ). خارج حاوية EJB3، يصبح EJB3 كائن Java قياسي. يتم تجاهل تعليقات EJB الخاصة به.

أعلاه، قمنا بتصوير Spring وحاوية EJB3 كبنية تحتية (إطار عمل) محتملة لهندستنا متعددة الطبقات. هذه البنية التحتية هي التي ستوفر الخدمات التي نحتاجها: تجمع الاتصالات ومدير المعاملات.

  • مع Spring، سيتم تنفيذ الطبقات باستخدام POJOs. وستتمتع هذه الطبقات بإمكانية الوصول إلى خدمات Spring (مجمع الاتصالات، مدير المعاملات) من خلال حقن التبعية في هذه POJOs: عند إنشائها، يقوم Spring بحقن مراجع إلى الخدمات التي ستحتاجها.
  • باستخدام حاوية EJB3، سيتم تنفيذ الطبقات باستخدام EJBs. لا تختلف البنية الطبقية المنفذة باستخدام EJB3s كثيرًا عن تلك المنفذة باستخدام POJOs التي تم إنشاء مثيلاتها بواسطة Spring. سنجد العديد من أوجه التشابه.
  • أخيرًا، سنقدم مثالًا لتطبيق ويب متعدد الطبقات: