2. بنية تطبيق Java متعدد الطبقات
غالبًا ما يتم تقسيم تطبيق Java إلى طبقات، لكل منها دور محدد بوضوح. لنأخذ بنية شائعة، وهي البنية ثلاثية الطبقات:
![]() |
- الطبقة [1]، المسماة هنا [ui] (واجهة المستخدم)، هي الطبقة التي تتفاعل مع المستخدم، عبر واجهة رسومية Swing أو واجهة وحدة التحكم أو واجهة الويب. وتتمثل مهمتها في توفير البيانات الواردة من المستخدم إلى الطبقة [2] أو عرض البيانات المقدمة من الطبقة [2] على المستخدم.
- الطبقة [2]، والمشار إليها هنا باسم [metier]، هي الطبقة التي تطبق القواعد المعروفة باسم قواعد العمل، c.a.d. المنطق الخاص بالتطبيق، دون الاهتمام بمصدر البيانات التي يتم تزويده بها، أو وجهة النتائج التي ينتجها.
- الطبقة [3]، المسماة هنا [DAO] (كائن الوصول إلى البيانات) هي الطبقة التي تزود الطبقة [2] ببيانات مسجلة مسبقًا (ملفات، قواعد بيانات، ...) وتسجل بعض النتائج التي توفرها الطبقة [2].
هناك إمكانيات مختلفة لتنفيذ الطبقة [DAO]. دعونا نستعرض بعضها:
![]() |
الطبقة [JDBC] المذكورة أعلاه هي الطبقة القياسية المستخدمة في Java للوصول إلى قواعد البيانات. وهي تعزل الطبقة [DAO] عن الطبقة SGBD التي تدير قاعدة البيانات. من الناحية النظرية، يمكن تغيير SGBD دون تغيير كود الطبقة [DAO]. على الرغم من هذه الميزة، فإن API و JDBC تنطوي على بعض العيوب:
- جميع العمليات على SGBD من المحتمل أن تطلق الاستثناء المراقب (checked) SQLException. وهذا يجبر الكود المستدعي (الطبقة [DAO] هنا) على إحاطة هذه العمليات بـ try / catch مما يجعل الكود ثقيلًا إلى حد ما.
- الطبقة [DAO] ليست غير حساسة تمامًا تجاه SGBD. فهذه الطبقات لديها، على سبيل المثال، طرق خاصة بها فيما يتعلق بالتوليد التلقائي لقيم المفاتيح الأولية التي لا يمكن للطبقة [DAO] تجاهلها. لذلك، عند إدراج سجل:
- مع Oracle، يجب أن تحصل الطبقة [DAO] أولاً على قيمة للمفتاح الأساسي للسجل ثم تقوم بإدراجه.
- مع SQL Server، تقوم الطبقة [DAO] بإدراج السجل الذي يُعطى تلقائيًا قيمة للمفتاح الأساسي بواسطة SGBD، وهي قيمة يتم تمريرها إلى الطبقة [DAO].
يمكن إزالة هذه الاختلافات باستخدام الإجراءات المخزنة. في المثال السابق، ستستدعي الطبقة [DAO] إجراءً مخزّنًا في Oracle أو SQL Server يأخذ في الاعتبار خصائص SGBD. وستكون هذه الخصائص مخفية في الطبقة [DAO]. ومع ذلك، إذا كان تغيير SGBD لا يتطلب إعادة كتابة الطبقة [DAO]، فإنه يتطلب مع ذلك إعادة كتابة الإجراءات المخزنة. وقد لا يُعتبر هذا عائقًا.
وقد بُذلت جهود متعددة لعزل الطبقة [DAO] عن الجوانب الخاصة بـ SGBD. ومن الحلول التي حققت نجاحًا حقيقيًا في هذا المجال خلال السنوات الأخيرة، حل Hibernate:
![]() |
تقع الطبقة [Hibernate] بين الطبقة [DAO] التي كتبها المطور والطبقة [JDBC]. Hibernate هو ORM (Object Relational Mapper)، وهو أداة تربط بين عالم قواعد البيانات العلائقية وعالم الكائنات التي تعالجها Java. لم يعد مطور الطبقة [DAO] يرى الطبقة [JDBC] ولا جداول قاعدة البيانات التي يريد استغلال محتواها. إنه لا يرى سوى الصورة الكائنية لقاعدة البيانات، وهي صورة كائنية توفرها الطبقة [Hibernate]. يتم الربط بين جداول قاعدة البيانات والكائنات التي تعالجها الطبقة [DAO] بشكل أساسي بطريقتين:
- من خلال ملفات التكوين من النوع XML
- عن طريق تعليقات Java في الكود، وهي تقنية متاحة فقط منذ الإصدار JDK 1.5
الطبقة [Hibernate] هي طبقة تجريدية تهدف إلى أن تكون شفافة قدر الإمكان. والهدف المثالي هو أن يتمكن مطور طبقة [DAO] من تجاهل تمامًا أنه يعمل مع قاعدة بيانات. وهذا أمر ممكن إذا لم يكن هو من يكتب التكوين الذي يشكل الجسر بين عالم العلاقات وعالم الكائنات. وتكوين هذا الجسر أمر دقيق إلى حد ما ويتطلب قدرًا معينًا من الخبرة.
يُطلق على طبقة الكائنات [4]، وهي صورة لطبقة BD، اسم "سياق الاستمرارية". تقوم طبقة [DAO] التي تعتمد على Hibernate بإجراء إجراءات الاستمرارية (CRUD، إنشاء - قراءة - تحديث - حذف) على كائنات سياق الاستمرارية، وهي إجراءات يترجمها Hibernate إلى أوامر SQL تنفذها الطبقة JDBC. بالنسبة لإجراءات الاستعلام عن قاعدة البيانات (SQL Select)، يوفر Hibernate للمطور لغة HQL (لغة استعلام Hibernate) لاستعلام سياق الاستمرارية [4] وليس BD نفسها.
يُعد 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] لا يؤثر على كود طبقة [métier]. يجب تعديل ملف تكوين Spring الذي يربط الطبقات ببعضها البعض فقط.
مع Java EE 5، هناك حل آخر: تنفيذ الطبقات [metier] و [DAO] باستخدام EJB3 (Enterprise Java Bean الإصدار 3):
![]() |
سنرى أن هذا الحل لا يختلف كثيرًا عن الحل الذي يستخدم Spring. تتوفر بيئة Java EE5 ضمن خوادم تُعرف بخوادم التطبيقات مثل Sun Application Server 9.x (Glassfish)، Jboss Application Server، Oracle Container for Java (OC4J)، ... خادم التطبيقات هو في الأساس خادم تطبيقات الويب. توجد أيضًا بيئات EE 5 تسمى "المستقلة"، c.a.d. يمكن استخدامها خارج خادم التطبيقات. وهذا هو الحال بالنسبة لـ JBoss و EJB3 أو OpenEJB.
في بيئة EE5، يتم تنفيذ الطبقات بواسطة كائنات تسمى EJB (Enterprise Java Bean). في الإصدارات السابقة من EE، كان يُعرف عن EJB (EJB 2.x) صعوبة تنفيذها واختبارها، وأحيانًا ضعف أدائها. يتم التمييز بين "entity" EJB2.x و"session" EJB2.x. باختصار، "entity" EJB2.x هو صورة لصف في جدول قاعدة بيانات و"session" EJB2.x هو كائن يُستخدم لتنفيذ طبقات [metier]، [DAO] في بنية متعددة الطبقات. أحد الانتقادات الرئيسية الموجهة إلى الطبقات التي تم تنفيذها باستخدام EJB هي أنها لا يمكن استخدامها إلا داخل حاويات EJB، وهي خدمة تقدمها بيئة EE. هذه البيئة، التي يعد تنفيذها أكثر تعقيدًا من بيئة SE (الإصدار القياسي)، قد تثني المطور عن إجراء الاختبارات بشكل متكرر. ومع ذلك، توجد بيئات تطوير Java تسهل استخدام خادم التطبيقات من خلال أتمتة نشر EJB على الخادم: Eclipse، Netbeans، JDeveloper، IntelliJ، IDEA. سنستخدم هنا Netbeans 6.8 وخادم التطبيقات Glassfish v3.
نشأ إطار عمل Spring كرد فعل على تعقيد EJB2. يوفر Spring في بيئة SE عددًا كبيرًا من الخدمات التي توفرها عادةً بيئات EE. وهكذا، في قسم "استمرارية البيانات"، يوفر Spring مجموعات الاتصال ومديري المعاملات التي تحتاجها التطبيقات. وقد ساهم ظهور Spring في تعزيز ثقافة الاختبارات الفردية، التي أصبحت أسهل في التنفيذ في سياق SE مقارنة بسياق EE. يسمح Spring بتنفيذ طبقات التطبيق باستخدام كائنات Java التقليدية (POJO، Plain Old/Ordinary Java Object)، مما يتيح إعادة استخدامها في سياق آخر. وأخيرًا، يدمج العديد من أدوات الجهات الخارجية بطريقة شفافة إلى حد ما، ولا سيما أدوات الاستمرارية مثل Hibernate وEclipseLink وIbatis، ...
تم تصميم Java EE5 لتصحيح أوجه القصور في مواصفات EJB2. أصبحت EJB و 2.x هي EJB3. وهذه هي POJOs الموسومة بتعليقات توضيحية تجعلها كائنات خاصة عندما تكون داخل حاوية EJB3. في هذا الحاوية، سيتمكن EJB3 من الاستفادة من خدمات الحاوية (مجموعة الاتصالات، مدير المعاملات، ...). خارج الحاوية EJB3، يصبح EJB3 كائن Java عادي. يتم تجاهل تعليقاته التوضيحية EJB.
فيما سبق، قمنا بتمثيل Spring وحاوية EJB3 كبنية تحتية (إطار عمل) محتملة لهندستنا متعددة الطبقات. هذه البنية التحتية هي التي ستوفر الخدمات التي نحتاجها: تجمع اتصالات ومدير معاملات.
- مع Spring، سيتم تنفيذ الطبقات باستخدام POJOs. وستتمكن هذه من الوصول إلى خدمات Spring (مجموعة الاتصالات، مدير المعاملات) عن طريق حقن التبعيات في هذه POJOs: عند إنشاء هذه، يقوم Spring بحقنها بمراجع للخدمات التي ستحتاجها.
- مع الحاوية EJB3، سيتم تنفيذ الطبقات باستخدام EJB. تختلف البنية الطبقية التي يتم تنفيذها باستخدام EJB3 قليلاً عن تلك التي يتم تنفيذها باستخدام POJO التي يتم إنشاء مثيلاتها بواسطة Spring. سنجد الكثير من أوجه التشابه.
- وأخيرًا، سنقدم مثالاً لتطبيق ويب متعدد الطبقات:
![]() |






