17. عرض قاعدة بيانات على الويب
17.1. بنية خدمة الويب / jSON
سنقوم بتنفيذ البنية التالية:
![]() |
- في [1]، الطبقات [DAO, [JPA], JDBC] يتم تنفيذها بواسطة إحدى التكوينات الـ 24 التي تم عرضها في الفصول السابقة، ولا سيما في الفقرة 15؛
- تنفذ الطبقة [DAO] [3] الخاصة بالعميل البعيد نفس الواجهة التي تنفذها الطبقة [DAO] [1]، مما يسمح لنا باستخدام نفس طبقة الاختبارات المستخدمة في الفصول السابقة. يبدو الأمر كما لو أن الطبقات [2-3] شفافة بالنسبة للطبقة [4]؛
سنعتمد على المشاريع التالية:
- المشروع [sgbd-config-jdbc] الذي يقوم بتكوين الطبقة JDBC لأحد المشاريع الستة SGBD؛
- المشروع [sgbd-config-jpa-*] الذي يقوم بتكوين الطبقة JPA الخاصة بـ SGBD المختار لإحدى التطبيقات الثلاثة JPA التي تمت دراستها (Hibernate، EclipseLink، OpenJpa)؛
- المشروع العام [spring-jdbc-04] الذي ينفذ الطبقة [DAO] [1]؛
- المشروع العام [spring-jpa-generic] الذي ينفذ الطبقة [DAO] و [2]؛
- المشروع العام [spring-webjson-server-jdbc-generic] الذي ينفذ خدمة ويب تعتمد على المشروع [spring-jdbc-04]؛
- المشروع العام [spring-webjson-server-jpa-generic] الذي ينفذ خدمة ويب تعتمد على المشروع [spring-jpa-generic]؛
- العميل العام [spring-webjson-client-generic] الذي سيكون العميل الوحيد لـ 24 تكوينًا لخدمة الويب؛
17.2. إعداد بيئة العمل
سنعمل مع العناصر التالية:
- SGBD MySQL 5.6.25؛
- تنفيذ JPA Hibernate؛
استيراد المشاريع التالية إلى STS:
![]() |
- ستجد المشاريع [spring-webjson-*] في المجلد [<exemples>\spring-database-generic\spring-webjson]؛
- قم بتنفيذ [Alt-F5] ثم أعد إنشاء جميع المشاريع المذكورة أعلاه؛
للتحقق من التثبيت الصحيح لبيئة العمل، اتبع الخطوات التالية:
- قم بتشغيل خدمة الويب باستخدام إعدادات التشغيل [spring-webjson-server-jpa-generic-hibernate] التي تعتمد على تطبيق JPA / Hibernate؛
![]() | ![]() |
ثم:
- قم بتشغيل عميل خدمة الويب هذه باستخدام تكوين التشغيل [spring-webjson-client-generic]، وهو اختبار لـ JUnit:
![]() | ![]() |
يجب أن ينجح الاختبار:
![]() |
- في [1]، أوقف خدمة الويب ثم قم بتشغيلها باستخدام تكوين التشغيل [spring-webjson-server-jdbc-generic] الذي يعتمد على تنفيذ JDBC:
![]() | ![]() |
ثم قم بتشغيل عميل خدمة الويب هذه باستخدام تكوين التشغيل [spring-webjson-client-generic]:
![]() | ![]() |
يجب أن ينجح الاختبار:
![]() |
17.3. تنفيذ خدمة الويب / jSON / JDBC
نبدأ أولاً بالاهتمام بالبنية التالية:
![]() |
حيث تتواصل الطبقة [DAO] [1] مباشرةً مع الطبقة JDBC التابعة لـ SGBD.
17.3.1. مشروع Eclipse لخدمة الويب
مشروع Eclipse لخدمة الويب / jSON / JDBC هو كما يلي:
![]() |
وهو مشروع Maven يحتوي على الملف [pom.xml] التالي:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>dvp.spring.database</groupId>
<artifactId>spring-webjson-server-jdbc-generic</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-webjson-server-jdbc-generic</name>
<description>démo spring mvc</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.3.RELEASE</version>
</parent>
<dependencies>
<!-- طبقة الويب -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- طبقة [DAO] -->
<dependency>
<groupId>dvp.spring.database</groupId>
<artifactId>spring-jdbc-generic-04</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<!-- المكونات الإضافية -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
</plugin>
</plugins>
</build>
</project>
- الأسطر 11-15: مشروع Maven الأصلي؛
- الأسطر 24-28: التبعية لطبقة [DAO / JDBC] التي ينفذها مشروع [spring-jdbc-generic-04]؛
- الأسطر 19-22: التبعية للأرتيفاكت [spring-boot-starter-web]. يجلب هذا الأرتيفاكت معه جميع التبعيات اللازمة لإنشاء خدمة ويب / jSON. كما يجلب مكتبات غير ضرورية. لذلك، سيكون من الضروري إجراء تكوين أكثر دقة، لكن هذا التكوين عملي للبدء.
التبعيات التي توفرها هذه التهيئة هي التالية:
![]() | ![]() | 1 ![]() |
- في [1]، نلاحظ أن Eclipse قد رصد التبعية على أرشيف مشروع [spring-jdbc-generic-04]؛
التبعيات المذكورة أعلاه هي تبعيات كل من الطبقة [DAO] والطبقة [web].
17.3.2. تكوين الطبقة [web]
يتم تكوين الطبقة [web] بواسطة ملفين من ملفات تكوين Spring:
![]() |
17.3.2.1. الفئة [WebConfig]
تتمثل المهمة الرئيسية للفئة [WebConfig] في تكوين:
- خادم Tomcat الذي سيتم نشر خدمة الويب عليه؛
- مرشحات jSON لتسلسل/إلغاء تسلسل الكائنات [Produit] و [Categorie]:
package spring.webjson.server.config;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
// -------------------------------- تكوين الطبقة [web]
@Autowired
private ApplicationContext context;
@Bean
public DispatcherServlet dispatcherServlet() {
DispatcherServlet servlet=new DispatcherServlet((WebApplicationContext) context);
return servlet;
}
@Bean
public ServletRegistrationBean servletRegistrationBean(DispatcherServlet dispatcherServlet) {
return new ServletRegistrationBean(dispatcherServlet, "/*");
}
@Bean
public EmbeddedServletContainerFactory embeddedServletContainerFactory() {
return new TomcatEmbeddedServletContainerFactory("", 8081);
}
// -------------------------------- تكوين المرشحات [json]
...
}
- السطر 25: الفئة هي فئة تكوين Spring؛
- السطر 26: تشير العلامة [@EnableWebMvc] إلى أن طبقة الويب مُنفَّذة باستخدام Spring MVC. سيؤدي ذلك إلى تكوينات ضمنية لن نضطر إلى القيام بها؛
- السطران 30-31: حقن سياق Spring للتطبيق؛
- الأسطر 33-37: تعريف البين [dispatcherServlet] الذي يؤدي، في تطبيقات Spring MVC، دور [FrontController] الذي تتمثل مهمته في توجيه طلبات العملاء إلى وحدة التحكم القادرة على معالجتها؛
- الأسطر 39-42: يتم تسجيل سيرفلت خدمة الويب بالإضافة إلى URL التي تعالجها. هنا تمت كتابة [/*]، وهو ما يعني جميع URLs؛
- الأسطر 44-47: تعريف البين [embeddedServletContainerFactory] الذي يحدد خادم الويب المطلوب استخدامه. هنا سيكون خادم الويب Tomcat [http://tomcat.apache.org/]. يمكن أيضًا استخدام خادم Jetty [http://www.eclipse.org/jetty/]. كلاهما خادمان مضمنان في تبعيات Maven. عندما يقوم [Spring Boot] بتشغيل المشروع، فإنه يقوم تلقائيًا بتشغيل خادم الويب المحدد في التكوين وينشر عليه الخدمة أو تطبيق الويب؛
تتم تهيئة مرشحات jSON بالطريقة التالية:
package spring.webjson.server.config;
import java.util.List;
...
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
// -------------------------------- تكوين الطبقة [web]
...
// -------------------------------- تكوين المرشحات [json]
// التعيين jSON
@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
final ObjectMapper objectMapper = new ObjectMapper();
converter.setObjectMapper(objectMapper);
return converter;
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(mappingJackson2HttpMessageConverter());
super.configureMessageConverters(converters);
}
// مرشحات jSON
@Bean
public ObjectMapper jsonMapper(MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter) {
return mappingJackson2HttpMessageConverter.getObjectMapper();
}
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
ObjectMapper jsonMapperShortCategorie(MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter) {
ObjectMapper jsonMapper = jsonMapper(mappingJackson2HttpMessageConverter);
jsonMapper.setFilters(new SimpleFilterProvider().addFilter("jsonFilterCategorie",
SimpleBeanPropertyFilter.serializeAllExcept("produits")));
return jsonMapper;
}
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
ObjectMapper jsonMapperLongCategorie(MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter) {
ObjectMapper jsonMapper = jsonMapper(mappingJackson2HttpMessageConverter);
jsonMapper.setFilters(new SimpleFilterProvider().addFilter("jsonFilterCategorie",
SimpleBeanPropertyFilter.serializeAllExcept()).addFilter("jsonFilterProduit",
SimpleBeanPropertyFilter.serializeAllExcept("categorie")));
return jsonMapper;
}
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
ObjectMapper jsonMapperShortProduit(MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter) {
ObjectMapper jsonMapper = jsonMapper(mappingJackson2HttpMessageConverter);
jsonMapper.setFilters(new SimpleFilterProvider().addFilter("jsonFilterProduit",
SimpleBeanPropertyFilter.serializeAllExcept("categorie")));
return jsonMapper;
}
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
ObjectMapper jsonMapperLongProduit(MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter) {
ObjectMapper jsonMapper = jsonMapper(mappingJackson2HttpMessageConverter);
jsonMapper.setFilters(new SimpleFilterProvider().addFilter("jsonFilterProduit",
SimpleBeanPropertyFilter.serializeAllExcept()).addFilter("jsonFilterCategorie",
SimpleBeanPropertyFilter.serializeAllExcept("produits")));
return jsonMapper;
}
}
- السطر 8: الفئة [WebConfig] تمتد من الفئة [WebMvcConfigurerAdapter]. هذه الفئة الأخيرة تهيئ تطبيق الويب بقيم افتراضية. عندما نرغب في تخصيص هذا التهيئة، يتعين إعادة تعريف بعض الأساليب في هذه الفئة. هنا، نريد إعادة تعريف الأسلوب [configureMessageConverters] في السطور [22-26] (لاحظ التعليق التوضيحي @Override) الذي يحدد قائمة بـ«المحولات». تتبادل خدمة الويب / jSON وعميلها سطور النص. المحول هو أداة قادرة على إنشاء كائن من سطر نصي مستلم (إلغاء التسلسل) وإنشاء سطر نصي من كائن (التسلسل). هنا، ستكون الأسطر النصية عبارة عن سلاسل jSON. لذلك سنتحدث عن التسلسل/إلغاء التسلسل لـ jSON؛
- السطر 23: تتلقى الطريقة [configureMessageConverters] قائمة بالمحولات كمعلمة؛
- السطران 24-25: يُضاف المحول jSON [MappingJackson2HttpMessageConverter] الخاص بالسطور [14-20] إلى هذه القائمة. سيسمح ذلك بالتبادل بين العميل والخادم؛
- السطور [14-20]: تحدد محولًا jSON يتم تنفيذه بواسطة الفئة [MappingJackson2HttpMessageConverter]. وستُوجد هذه الفئة (السطر 10) في تبعيات Maven الخاصة بالمشروع؛
- الأسطر [17-18]: يتم إنشاء مُعَيِّن jSON وتعيينه للمحول [MappingJackson2HttpMessageConverter]؛
- الأسطر [29-32]: تُعرّف أداة التعيين jSON التي تم إنشاؤها في السطر 17 كـ«بيان» في Spring. وهذا يضعها في سياق Spring ويجعلها متاحة للحقن في «بيانات» أخرى أو لاستخدامها في كود تطبيق الويب؛
- الأسطر 34-41: تُعرّف مرشحًا jSON لمُعيّن الخرائط jSON السابق؛
- السطر 35: تعمل العلامة التوضيحية [@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)] على جعل الكائن المُعرَّف هنا غير فردي (singleton). في كل مرة يُطلب فيها من السياق، سيتم إعادة تنفيذ الطريقة [jsonMapperCategorieWithoutProduits]. وهذا ضروري هنا لأننا نُعرِّف أربعة مرشحات jSON. لكن يجب أن يكون مرشح واحد فقط نشطًا في أي وقت معين. ومن خلال منح «البيان» نطاق [ConfigurableBeanFactory.SCOPE_PROTOTYPE]، نضمن إعادة تنفيذ الطريقة واستبدال المرشح السابق بالمرشح الجديد؛
- لفهم هذه المرشحات، يجب أن نتذكر أنه في الطبقة [DAO]:
- تم تزيين الكيان [Produit] بالتعليق التوضيحي [jsonFilterProduit]؛
- تم تزيين الكيان [Categorie] بالتعليق التوضيحي [jsonFilterCategorie]؛
لذلك يجب تعريف مرشحات تحمل هذه الأسماء.
- السطور [34-41]: تُعرِّف مرشحًا يُسمى [jsonMapperShortCategorie]، والذي يتيح الحصول على تمثيل jSON لفئة ما بدون منتجاتها؛
- السطور [43-51]: تحدد مرشحًا يُسمى [jsonMapperLongCategorie]، والذي يتيح الحصول على تمثيل jSON لفئة مع منتجاتها؛
- السطور [53-60]: تحدد مرشحًا يُسمى [jsonMapperShortProduit] يتيح الحصول على تمثيل jSON لمنتج بدون فئته؛
- السطور [62-70]: تحدد مرشحًا يُسمى [jsonMapperLongProduit] يتيح الحصول على التمثيل jSON لمنتج مع فئته؛
17.3.2.2. الفئة [AppConfig]
تقوم الفئة [AppConfig] بتكوين التطبيق بأكمله، أي الطبقتين [web] و [DAO]:
package spring.webjson.server.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@ComponentScan(basePackages = { "spring.webjson.server.service" })
@Import({ spring.jdbc.config.AppConfig.class, WebConfig.class })
public class AppConfig {
}
- السطر 7: الفئة هي فئة تكوين Spring؛
- السطر 9: يتم استيراد حبات الطبقة [DAO / JDBC] بالإضافة إلى تلك المُعرَّفة بواسطة الفئة [WebConfig]. وبالتالي، ستكون جميع البينات الموجودة في الطبقة [DAO] متاحة في تطبيق الويب / jSON؛
- السطر 8: يشير إلى الحزم التي يمكن العثور فيها على مكونات Spring الأخرى؛
17.3.3. الاستثناء [ServerException]
![]() |
وكما في الفصول السابقة، كانت الطبقة [DAO] تُطلق استثناءً غير مُراقب [DaoException]، وستُطلق الطبقة [web] استثناءً غير مُراقب [ServerException]:
package spring.webjson.server.infrastructure;
import generic.jdbc.infrastructure.UncheckedException;
public class ServerException extends UncheckedException {
private static final long serialVersionUID = 1L;
// الشركات المصنعة
public ServerException() {
super();
}
public ServerException(int code, Throwable e, String simpleClassName) {
super(code, e, simpleClassName);
}
}
- السطر 5: الفئة [ServerException] تمتد من الفئة [UncheckedException] المُعرَّفة في المشروع الذي يُكوِّن الطبقة JDBC (السطر 3)؛
17.3.4. وحدات التحكم
![]() |
![]() |
سيكون لدينا هنا وحدتا تحكم:
- سيتحكم [CategorieController] في الاستعلامات المتعلقة بالفئات؛
- [CategorieController] سيتحكم في الاستعلامات المتعلقة بالمنتجات؛
تتوافق وحدات التحكم URL المعروضة مع كل طريقة من طرق واجهات [DaoCategorie] و [DaoProduit] في الطبقة [DAO]:
![]() | ![]() |
وبالتالي، كما هو موضح أعلاه:
- ستستدعي طريقة الويب [deleteAllCategories] الطريقة [deleteAllEntities] من الفئة [DaoCategorie]؛
- ستستدعي طريقة الويب [getShortCategoriesById] الطريقة [getShortEntitiesById] من الفئة [DaoCategorie]؛
وينطبق الأمر نفسه على المنتجات:
![]() | ![]() |
17.3.4.1. المنتجات URL التي يعرضها وحدة التحكم [CategorieController]
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
17.3.4.2. URL المعروضة بواسطة وحدة التحكم [ProduitController]
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
17.3.5. التنفيذ العام لخدمة الويب
![]() |
تُظهر قائمة URL التي تعرضها خدمة الويب أننا نقدم نفس أنواع URL لإدارة الفئات والمنتجات. بدلاً من كتابة وحدة تحكمين متشابهتين للغاية، سنجعلهما مشتقتين من فئة تقوم بكل المهام المشتركة بين الوحدتين. وستكون هذه الفئة هي [AbstractController] المذكورة أعلاه. وستقوم هذه الفئة بتنفيذ الواجهة التالية:
package spring.webjson.server.service;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import spring.jdbc.entities.AbstractCoreEntity;
public interface Iws<T extends AbstractCoreEntity> {
// قائمة بجميع الكيانات T
public Response<List<T>> getAllShortEntities();
public Response<List<T>> getAllLongEntities();
// للكيانات المحددة - النسخة المختصرة
public Response<List<T>> getShortEntitiesById(HttpServletRequest request);
public Response<List<T>> getShortEntitiesByName(HttpServletRequest request);
// الكيانات المحددة - النسخة الطويلة
public Response<List<T>> getLongEntitiesById(HttpServletRequest request);
public Response<List<T>> getLongEntitiesByName(HttpServletRequest request);
// تحديث عدة كيانات
public Response<List<T>> saveEntities(HttpServletRequest request);
// حذف جميع الكيانات
public Response<Void> deleteAllEntities();
// حذف عدة كيانات
public Response<Void> deleteEntitiesById(HttpServletRequest request);
public Response<Void> deleteEntitiesByName(HttpServletRequest request);
}
تتضمن هذه الواجهة الأساليب الموجودة في واجهة الطبقة [DAO] التي سيتم استخدامها:
package spring.jdbc.dao;
import java.util.List;
import spring.jdbc.entities.AbstractCoreEntity;
public interface IDao<T extends AbstractCoreEntity> {
// قائمة بجميع الكيانات T
public List<T> getAllShortEntities();
public List<T> getAllLongEntities();
// للكيانات المحددة - النسخة المختصرة
public List<T> getShortEntitiesById(Iterable<Long> ids);
public List<T> getShortEntitiesById(Long... ids);
public List<T> getShortEntitiesByName(Iterable<String> names);
public List<T> getShortEntitiesByName(String... names);
// الكيانات المحددة - النسخة الطويلة
public List<T> getLongEntitiesById(Iterable<Long> ids);
public List<T> getLongEntitiesById(Long... ids);
public List<T> getLongEntitiesByName(Iterable<String> names);
public List<T> getLongEntitiesByName(String... names);
// تحديث عدة كيانات
public List<T> saveEntities(Iterable<T> entities);
public List<T> saveEntities(@SuppressWarnings("unchecked") T... entities);
// حذف جميع الكيانات
public void deleteAllEntities();
// حذف عدة كيانات
public void deleteEntitiesById(Iterable<Long> ids);
public void deleteEntitiesById(Long... ids);
public void deleteEntitiesByName(Iterable<String> names);
public void deleteEntitiesByName(String... names);
public void deleteEntitiesByEntity(Iterable<T> entities);
public void deleteEntitiesByEntity(@SuppressWarnings("unchecked") T... entities);
}
وقد اتبع الانتقال من واجهة [IDao<T>] في الطبقة [DAO] إلى واجهة [Iws<T>] لخدمة الويب القواعد التالية:
- لن تطلق أساليب واجهة [Iws<T>] أي استثناء. وفي حالة وجود استثناء، فسيتم تغليفه في الكائن [Response]؛
- تختفي المتغيرات الخاصة بالمعلمات، مثل السطرين 45 و47 في [Iterable<String> names, String... names]. تحصل الطرق على معلماتها من الاستعلام HTTP الصادر عن العميل من النوع [HttpServletRequest request]؛
سيتم تغليف جميع استجابات خدمة الويب في الكائن [Response] التالي:
![]() |
package spring.webjson.server.service;
public class Response<T> {
// ----------------- الخصائص
// حالة العملية
private int status;
// رسالة خطأ
private String exception;
// نص الرد
private T body;
// المنشئات
public Response() {
}
public Response(int status, String exception, T body) {
this.status = status;
this.exception = exception;
this.body = body;
}
// أدوات الاسترجاع والتعيين
...
}
- السطر 4: الرد يحتوي على نوع T؛
- السطر 12: الرد من النوع T؛
- الأسطر 7-10: من الممكن أن تواجه إحدى الطرق استثناءً. في هذه الحالة، ستُرجع استجابةً تحتوي على:
- السطر 8: status!=0؛
- السطر 10: رسالة خطأ؛
الفئة [AbstractController] هي كما يلي:
package spring.webjson.server.service;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import spring.jdbc.dao.IDao;
import spring.jdbc.entities.AbstractCoreEntity;
import spring.jdbc.infrastructure.DaoException;
import spring.webjson.server.infrastructure.ServerException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.io.CharStreams;
public abstract class AbstractController<T extends AbstractCoreEntity> implements Iws<T> {
@Autowired
protected ApplicationContext context;
// طبقة DAO
private IDao<T> dao;
abstract protected IDao<T> getDao();
// محلي
private String simpleClassName = getClass().getSimpleName();
@PostConstruct
public void init(){
dao=getDao();
}
@Override
public Response<List<T>> getAllShortEntities() {
try {
// الرد
return new Response<List<T>>(0, null, dao.getAllShortEntities());
} catch (DaoException e) {
return new Response<List<T>>(1, e.toString(), null);
} catch (Exception e) {
return new Response<List<T>>(2, new ServerException(1007, e, simpleClassName).toString(), null);
}
}
@Override
public Response<List<T>> getAllLongEntities() {
try {
// الاستجابة
return new Response<List<T>>(0, null, dao.getAllLongEntities());
} catch (DaoException e) {
return new Response<List<T>>(1, e.toString(), null);
} catch (Exception e) {
return new Response<List<T>>(2, new ServerException(1008, e, simpleClassName).toString(), null);
}
}
@Override
public Response<List<T>> getShortEntitiesById(HttpServletRequest request) {
...
}
@Override
public Response<List<T>> getShortEntitiesByName(HttpServletRequest request) {
...
}
@Override
public Response<List<T>> getLongEntitiesById(HttpServletRequest request) {
...
}
@Override
public Response<List<T>> getLongEntitiesByName(HttpServletRequest request) {
...
}
@Override
public Response<List<T>> saveEntities(HttpServletRequest request) {
return new Response<List<T>>(2, new ServerException(1013, new RuntimeException("[saveEntities] not implemented"), simpleClassName).toString(), null);
}
@Override
public Response<Void> deleteAllEntities() {
try {
// يتم الحذف
dao.deleteAllEntities();
// الرد
return new Response<Void>(0, null, null);
} catch (DaoException e) {
return new Response<Void>(1, e.toString(), null);
} catch (Exception e) {
return new Response<Void>(2, new ServerException(1014, e, simpleClassName).toString(), null);
}
}
@Override
public Response<Void> deleteEntitiesById(HttpServletRequest request) {
...
}
@Override
public Response<Void> deleteEntitiesByName(HttpServletRequest request) {
...
}
}
يتم تنفيذ جميع الطرق بنفس الطريقة:
- إذا كانت تتوقع معلومات، فإنها تستردها من الكائن [HttpServletRequest request]؛
- تستدعي الطريقة في الطبقة [DAO] التي تحمل نفس اسمها؛
- تتعامل مع الاستثناءات التي قد تحدث، إما في العملية 1 لاسترداد المعلمات، أو في العملية 2 لاستدعاء الطبقة [DAO]؛
لننظر أولاً في كيفية إدخال الطبقة [DAO] في الفئة [AbstractController]:
public abstract class AbstractController<T extends AbstractCoreEntity> implements Iws<T> {
@Autowired
protected ApplicationContext context;
// طبقة DAO
private IDao<T> dao;
abstract protected IDao<T> getDao();
@PostConstruct
public void init(){
dao=getDao();
}
- السطر 1: الفئة مجردة وتنفذ الواجهة العامة [Iws<T>]؛
- السطران 3-4: حقن سياق Spring؛
- السطر 7: المرجع غير المعروف بعد للطبقة [DAO] المطلوب استخدامها؛
- السطر 9: الطريقة المجردة [getDao] التي ستُرجع مرجع الطبقة [DAO] المطلوب استخدامها. سيتم إعادة تعريف هذه الطريقة بواسطة الفئة الفرعية، وبالتالي فإن الفئة الفرعية هي التي ستحدد الطبقة [DAO] التي يجب استخدامها (DaoProduit أو DaoCategorie)؛
- السطر 11: التعليق التوضيحي [@PostConstruct] يحدد طريقة يجب تنفيذها عند انتهاء إنشاء مثيل الكائن. وعند انتهاء إنشاء المثيل، تكون عمليات الحقن في Spring قد تمت. وستكون الفئة الفرعية قد حصلت عندئذٍ على مرجع طبقتها [DAO]، وبالتالي ستتمكن من تمريره إلى الفئة الأم؛
والطريقة [getShortEntitiesById] هي كما يلي:
@Override
public Response<List<T>> getShortEntitiesById(HttpServletRequest request) {
try {
// يتم استرداد القيمة المرسلة
String body = CharStreams.toString(request.getReader());
// إلغاء التسلسل
ObjectMapper mapper = context.getBean("jsonMapper", ObjectMapper.class);
List<Long> ids = mapper.readValue(body, new TypeReference<List<Long>>() {
});
// الرد
return new Response<List<T>>(0, null, dao.getShortEntitiesById(ids));
} catch (DaoException e) {
return new Response<List<T>>(1, e.toString(), null);
} catch (Exception e) {
return new Response<List<T>>(2, new ServerException(1009, e, simpleClassName).toString(), null);
}
}
- السطر 5: ستكون القيمة المرسلة من العميل سلسلة jSON. وهنا يتم استردادها؛
- الأسطر 7-9: تحتوي السلسلة jSON على قائمة بالمفاتيح الأولية للكيانات التي نريد الحصول على نسخة مختصرة منها؛
- السطر 11: يتم استدعاء الطريقة [DAO] التي تحمل الاسم نفسه. يتم تغليف الاستجابة من النوع [List<T>] في كائن [Response]؛
- السطر 13: الحالة التي أطلقت فيها الطبقة [DAO] استثناءً؛
- السطر 15: حالة الاستثناءات الأخرى، ولا سيما الاستثناء المحتمل عند إزالة التسلسل للمعلمة jSON، السطر 8؛
الطريقة [getShortEntitiesByName] مماثلة:
@Override
public Response<List<T>> getShortEntitiesByName(HttpServletRequest request) {
try {
// يتم استرداد القيمة المرسلة
String body = CharStreams.toString(request.getReader());
// يتم تحويلها إلى بيانات
ObjectMapper mapper = context.getBean("jsonMapper", ObjectMapper.class);
List<String> noms = mapper.readValue(body, new TypeReference<List<String>>() {
});
// الرد
return new Response<List<T>>(0, null, dao.getShortEntitiesByName(noms));
} catch (DaoException e) {
return new Response<List<T>>(1, e.toString(), null);
} catch (Exception e) {
return new Response<List<T>>(2, new ServerException(1010, e, simpleClassName).toString(), null);
}
}
- الأسطر 4-9: هنا، المعلمة jSON هي قائمة بأسماء الفئات التي نريد الحصول على نسخة مختصرة منها؛
لم يتم تنفيذ الطريقة [saveEntities] لأنها تعتمد بشكل كبير على طبيعة الكيان المراد الاحتفاظ به، سواء كان [Categorie] أو [Produit]. ولا يوجد سوى القليل من التعليمات البرمجية التي يمكن فصلها. ولذلك، يُترك هذا العمل للفئات الفرعية.
@Override
public Response<List<T>> saveEntities(HttpServletRequest request) {
return new Response<List<T>>(2, new ServerException(1013, new RuntimeException("[saveEntities] not implemented"), simpleClassName).toString(), null);
}
17.3.6. وحدة التحكم [CategorieController]
![]() |
تتولى وحدة التحكم [CategorieController] معالجة URL فيما يتعلق بالفئات التالية:
package spring.webjson.server.service;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import spring.jdbc.dao.IDao;
import spring.jdbc.entities.Categorie;
import spring.jdbc.entities.Produit;
import spring.jdbc.infrastructure.DaoException;
import spring.webjson.server.entities.CoreCategorie;
import spring.webjson.server.entities.CoreProduit;
import spring.webjson.server.infrastructure.ServerException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.io.CharStreams;
@RestController
public class CategorieController extends AbstractController<Categorie> {
@Autowired
private IDao<Categorie> daoCategorie;
@Override
protected IDao<Categorie> getDao() {
return daoCategorie;
}
// محلي
private String simpleClassName = getClass().getSimpleName();
@RequestMapping(value = "/getAllShortCategories", method = RequestMethod.GET)
public Response<List<Categorie>> getAllShortCategories() {
// الأصل
Response<List<Categorie>> response = super.getAllShortEntities();
// مرشحات التسلسل jSON
context.getBean("jsonMapperShortCategorie", ObjectMapper.class);
// الرد
return response;
}
@RequestMapping(value = "/getAllLongCategories", method = RequestMethod.GET)
public Response<List<Categorie>> getAllLongCategories() {
// الأصل
Response<List<Categorie>> response = super.getAllLongEntities();
// مرشحات التسلسل jSON
context.getBean("jsonMapperLongCategorie", ObjectMapper.class);
// الرد
return response;
}
@RequestMapping(value = "/getShortCategoriesById", method = RequestMethod.POST)
public Response<List<Categorie>> getShortCategoriesById(HttpServletRequest request) {
// الأصل
Response<List<Categorie>> response = super.getShortEntitiesById(request);
// مرشحات التسلسل jSON
context.getBean("jsonMapperShortCategorie", ObjectMapper.class);
// الرد
return response;
}
@RequestMapping(value = "/getShortCategoriesByName", method = RequestMethod.POST)
public Response<List<Categorie>> getShortCategoriesByName(HttpServletRequest request) {
// الأصل
Response<List<Categorie>> response = super.getShortEntitiesByName(request);
// مرشحات التسلسل jSON
context.getBean("jsonMapperShortCategorie", ObjectMapper.class);
// الرد
return response;
}
@RequestMapping(value = "/getLongCategoriesById", method = RequestMethod.POST)
public Response<List<Categorie>> getLongCategoriesById(HttpServletRequest request) {
// الأصل
Response<List<Categorie>> response = super.getLongEntitiesById(request);
// مرشحات التسلسل jSON
context.getBean("jsonMapperLongCategorie", ObjectMapper.class);
// الرد
return response;
}
@RequestMapping(value = "/getLongCategoriesByName", method = RequestMethod.POST)
public Response<List<Categorie>> getLongCategoriesByName(HttpServletRequest request) {
// الأصل
Response<List<Categorie>> response = super.getLongEntitiesByName(request);
// مرشحات التسلسل jSON
context.getBean("jsonMapperLongCategorie", ObjectMapper.class);
// الرد
return response;
}
@RequestMapping(value = "/saveCategories", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8")
public Response<List<CoreCategorie>> saveCategories(HttpServletRequest request) {
...
}
@RequestMapping(value = "/deleteAllCategories", method = RequestMethod.GET)
public Response<Void> deleteAllCategories() {
return super.deleteAllEntities();
}
@RequestMapping(value = "/deleteCategoriesById", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8")
public Response<Void> deleteCategoriesById(HttpServletRequest request) {
return super.deleteEntitiesById(request);
}
@RequestMapping(value = "/deleteCategoriesByName", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8")
public Response<Void> deleteCategoriesByName(HttpServletRequest request) {
return super.deleteEntitiesByName(request);
}
}
- السطر 26: الفئة [CategorieController] تمتد من الفئة [AbstractController]؛
- السطر 25: التعليق التوضيحي [@RestController] يجعل الفئة مكونًا في Spring. ويشير هذا التعليق التوضيحي أيضًا إلى أن الفئة هي خدمة ويب ترسل أساليبها استجاباتها مباشرةً إلى العميل بتنسيق jSON؛
- السطران 28-29: يتم هنا حقن مرجع الطبقة [DAO]؛
- الأسطر 31-34: إعادة تعريف الطريقة [getDao] التي تم إعلانها على أنها طريقة مجردة في الفئة الأم، والغرض منها هو إرجاع مرجع إلى الطبقة [DAO] لاستخدامها؛
جميع الطرق مبنية على نفس النموذج:
- تفويض المعالجة إلى الفئة الأم؛
- تهيئة أداة التعيين jSON التي ستقوم بتسلسل الاستجابة؛
- إرسال الاستجابة؛
لنلقِ نظرة على توقيع بعض URL:
| - يتم استدعاء URL [/getAllShortCategories] باستخدام GET |
| - يتم استدعاء URL [/getShortCategoriesById] باستخدام POST. القيمة التي يتم إرسالها هي السلسلة jSON للمفاتيح الأساسية الفئات المطلوبة؛ |
| - يتم استدعاء URL [/getLongCategoriesByName] باستخدام POST. القيمة التي يتم إرسالها هي السلسلة jSON التي تحتوي على أسماء الفئات المطلوبة؛ |
الآن، دعونا نستعرض بالتفصيل الدالة [saveCategories] التي لا تتبع تنسيق الدوال الأخرى:
@RequestMapping(value = "/saveCategories", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8")
public Response<List<CoreCategorie>> saveCategories(HttpServletRequest request) {
// يتم الاحتفاظ بالفئات
try {
// استرداد القيمة المرسلة
String body = CharStreams.toString(request.getReader());
// إلغاء التسلسل
ObjectMapper mapper = context.getBean("jsonMapperLongCategorie", ObjectMapper.class);
List<Categorie> categories = mapper.readValue(body, new TypeReference<List<Categorie>>() {
});
// يتم حفظ الفئات
categories = daoCategorie.saveEntities(categories);
// عرض النتيجة
List<CoreCategorie> coreCategories = new ArrayList<CoreCategorie>();
for (Categorie categorie : categories) {
CoreCategorie coreCategorie = new CoreCategorie(categorie.getId());
coreCategories.add(coreCategorie);
List<Produit> produits = categorie.getProduits();
if (produits != null) {
List<CoreProduit> coreProduits = new ArrayList<CoreProduit>();
for (Produit produit : categorie.getProduits()) {
coreProduits.add(new CoreProduit(produit.getId()));
}
coreCategorie.setCoreProduits(coreProduits);
}
}
// النتيجة
return new Response<List<CoreCategorie>>(0, null, coreCategories);
} catch (DaoException e) {
return new Response<List<CoreCategorie>>(1, e.toString(), null);
} catch (Exception e) {
return new Response<List<CoreCategorie>>(2, new ServerException(1020, e, simpleClassName).toString(), null);
}
}
- السطر 1: تُرفق القيمة URL [/saveCategories] بقيمة مُرسلة. وهذه القيمة هي السلسلة jSON التي تحتوي على النسخ الطويلة للفئات المطلوب الاحتفاظ بها؛
- الأسطر 5-10: يتم إعادة إنشاء الفئات المطلوب الاحتفاظ بها انطلاقًا من السلسلة jSON. الرابط [produit.categorie] الذي يربط [Produit] بـ [Catégorie] يساوي null لأن في النسخة الطويلة من [Categorie]، يكون كل [Produit] هو نفسه في نسخته المختصرة بدون حقل [categorie]. ولا يشكل ذلك عائقًا، لأن الطبقة [DAO] التي تم تنفيذها باستخدام JDBC لا تحتاج إلى هذه المعلومات؛
- السطر 12: يتم حفظ الفئات. تمت إثراء قائمة الفئات المستلمة بالمفاتيح الأولية للعناصر المحفوظة، وهي الفئات والمنتجات. لم يتغير أي شيء آخر. بدلاً من إرجاع القائمة المستلمة بالكامل، وهو ما ينطوي على تكلفة، سنقوم بإرجاع المفاتيح الأولية لعناصر هذه القائمة فقط. ولهذا الغرض، نستخدم الفئتين التاليتين: [CoreCategorie] و [CoreProduit]:
![]() |
package spring.webjson.server.entities;
import java.util.List;
public class CoreCategorie {
// المفتاح الأساسي
private Long id;
// الشركات المصنعة
public CoreCategorie() {
}
public CoreCategorie(Long id) {
this.id=id;
}
// قائمة المنتجات
private List<CoreProduit> coreProduits;
// أدوات الاسترجاع والتعيين
...
}
- السطر 8: المفتاح الأساسي لأحد المنتجات؛
- السطر 20: المفاتيح الأولية لمنتجاته؛
package spring.webjson.server.entities;
public class CoreProduit {
// المفتاح الأساسي
private Long id;
// منشئات
public CoreProduit() {
}
public CoreProduit(Long id) {
this.id = id;
}
// دالات الاسترجاع والتعيين
...
}
- السطر 6: المفتاح الأساسي لأحد المنتجات؛
لنعد إلى كود الطريقة [saveCategories]:
...
// نقوم بحفظ الفئات
categories = daoCategorie.saveEntities(categories);
// عرض النتيجة
List<CoreCategorie> coreCategories = new ArrayList<CoreCategorie>();
for (Categorie categorie : categories) {
CoreCategorie coreCategorie = new CoreCategorie(categorie.getId());
coreCategories.add(coreCategorie);
List<Produit> produits = categorie.getProduits();
if (produits != null) {
List<CoreProduit> coreProduits = new ArrayList<CoreProduit>();
for (Produit produit : categorie.getProduits()) {
coreProduits.add(new CoreProduit(produit.getId()));
}
coreCategorie.setCoreProduits(coreProduits);
}
}
// النتيجة
return new Response<List<CoreCategorie>>(0, null, coreCategories);
...
- الأسطر 5-17: يتم إنشاء قائمة بـ [CoreCategorie] التي سيتم إرجاعها إلى العميل البعيد؛
- السطر 19: يتم إرجاع الاستجابة وتسلسلها في jSON؛
17.3.7. إدارة المرشحات jSON
بالنسبة لكل طريقة في وحدة التحكم، هناك مرحلتان لعملية التسلسل/إلغاء التسلسل jSON:
- إلغاء تسلسل القيمة المرسلة: تتم إدارته هنا بشكل صريح؛
- التسلسل للنتيجة: يتم إدارته هنا ضمناً؛
لنبدأ بإلغاء التسلسل للقيمة المرسلة في [CategorieController.saveCategories]:
@RequestMapping(value = "/saveCategories", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8")
public Response<List<CoreCategorie>> saveCategories(HttpServletRequest request) {
// يتم حفظ الفئات
try {
// استرداد القيمة المرسلة
String body = CharStreams.toString(request.getReader());
// إلغاء تسلسلها
ObjectMapper mapper = context.getBean("jsonMapperLongCategorie", ObjectMapper.class);
List<Categorie> categories = mapper.readValue(body, new TypeReference<List<Categorie>>() {
});
- السطر 8: يتم استرداد مُعَيِّن مُهيأ لإدارة المرشح jSON [jsonMapperLongCategorie] من سياق Spring. لنعد إلى تعريف هذا المُعَيِّن في فئة التكوين [WebConfig]:
// -------------------------------- تكوين المرشحات [json]
// التعيين jSON
@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
final ObjectMapper objectMapper = new ObjectMapper();
converter.setObjectMapper(objectMapper);
return converter;
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(mappingJackson2HttpMessageConverter());
super.configureMessageConverters(converters);
}
// مرشحات jSON
@Bean
public ObjectMapper jsonMapper(MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter) {
return mappingJackson2HttpMessageConverter.getObjectMapper();
}
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
ObjectMapper jsonMapperShortCategorie(MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter) {
ObjectMapper jsonMapper = jsonMapper(mappingJackson2HttpMessageConverter);
jsonMapper.setFilters(new SimpleFilterProvider().addFilter("jsonFilterCategorie",
SimpleBeanPropertyFilter.serializeAllExcept("produits")));
return jsonMapper;
}
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
ObjectMapper jsonMapperLongCategorie(MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter) {
ObjectMapper jsonMapper = jsonMapper(mappingJackson2HttpMessageConverter);
jsonMapper.setFilters(new SimpleFilterProvider().addFilter("jsonFilterCategorie",
SimpleBeanPropertyFilter.serializeAllExcept()).addFilter("jsonFilterProduit",
SimpleBeanPropertyFilter.serializeAllExcept("categorie")));
return jsonMapper;
}
- الأسطر 32-40: المُعَيِّن jSON [jsonMapperLongCategorie] الذي تم استرداده بواسطة الفئة [CategorieController]؛
- السطر 35: يتم عرض أداة التعيين هذه بواسطة الطريقة [jsonMapper] الواردة في الأسطر 18-21؛
- الأسطر 18-21: تعرض الطريقة [jsonMapper] المُخطِّط jSON الخاص بالمحول [MappingJackson2HttpMessageConverter] الوارد في الأسطر 3-9؛
بعبارة أخرى، أداة التعيين jSON التي تم استردادها بواسطة السطر 4 أدناه في [CategorieController.saveCategories]:
// يتم استرداد القيمة المرسلة
String body = CharStreams.toString(request.getReader());
// يتم تحويلها من صيغة تسلسلية إلى صيغة ثنائية
ObjectMapper mapper = context.getBean("jsonMapperLongCategorie", ObjectMapper.class);
List<Categorie> categories = mapper.readValue(body, new TypeReference<List<Categorie>>() {
});
هو المحول الذي يستخدمه Spring MVC افتراضيًا لإلغاء تسلسل القيمة المرسلة من العميل وتسلسل النتيجة المرسلة إليه. في الأسطر أعلاه، لم يتم إجراء عملية إلغاء تسلسل ضمنية للقيمة المرسلة. وللقيام بذلك، كان يجب كتابة:
@RequestMapping(value = "/saveCategories", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8")
public Response<List<CoreCategorie>> saveCategories(@RequestBody List<Categorie> categories) {
في هذه الحالة، كان سيتم تحويل القيمة المرسلة في المعلمة [categories] إلى صيغة غير تسلسلية تلقائيًا. لكن كانت هناك مشكلة تتعلق بمرشح [jsonFilterCategorie] الذي تمتلكه الكيانات [Categorie]. لذا يجب تهيئته. ولهذا السبب، اخترنا عملية إزالة التسلسل الصريحة (السطران 4-5). النقطة الثانية التي يجب ملاحظتها هي أن أداة التعيين في السطر 4 (وهي الأداة التي يستخدمها Spring MVC افتراضيًا) مناسبة أيضًا لتسلسل النتيجة [Response<List<CoreCategorie>]. في الواقع، الكيان [CoreCategorie] لا يحتوي على مرشح jSON. وبالتالي، لا داعي لتكوين أداة التعيين jSON التي تم الحصول عليها باستخدام مرشح إضافي. وسيتم في هذه الحالة تسلسل ضمني للاستجابة المرسلة إلى العميل.
17.3.8. وحدة التحكم [ProduitController]
![]() |
تتولى وحدة التحكم [ProduitController] معالجة URL المتعلقة بالمنتجات. ويشبه كودها كود وحدة التحكم [CategorieController]:
package spring.webjson.server.service;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import spring.jdbc.dao.IDao;
import spring.jdbc.entities.Produit;
import spring.jdbc.infrastructure.DaoException;
import spring.webjson.server.entities.CoreProduit;
import spring.webjson.server.infrastructure.ServerException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.io.CharStreams;
@RestController
public class ProduitController extends AbstractController<Produit> {
@Autowired
private IDao<Produit> daoProduit;
@Override
protected IDao<Produit> getDao() {
return daoProduit;
}
// محلي
private String simpleClassName = getClass().getSimpleName();
@RequestMapping(value = "/getAllShortProduits", method = RequestMethod.GET)
public Response<List<Produit>> getAllShortProduits() {
// الأصل
Response<List<Produit>> response = super.getAllShortEntities();
// مرشحات التسلسل jSON
context.getBean("jsonMapperShortProduit", ObjectMapper.class);
// الرد
return response;
}
@RequestMapping(value = "/getAllLongProduits", method = RequestMethod.GET)
public Response<List<Produit>> getAllLongProduits() {
// الأصل
Response<List<Produit>> response = super.getAllLongEntities();
// مرشحات التسلسل jSON
context.getBean("jsonMapperLongProduit", ObjectMapper.class);
// الرد
return response;
}
@RequestMapping(value = "/getShortProduitsById", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8")
public Response<List<Produit>> getShortProduitsById(HttpServletRequest request) {
// الأصل
Response<List<Produit>> response = super.getShortEntitiesById(request);
// مرشحات التسلسل jSON
context.getBean("jsonMapperShortProduit", ObjectMapper.class);
// الرد
return response;
}
@RequestMapping(value = "/getShortProduitsByName", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8")
public Response<List<Produit>> getShortProduitsByName(HttpServletRequest request) {
// الأصل
Response<List<Produit>> response = super.getShortEntitiesByName(request);
// مرشحات التسلسل jSON
context.getBean("jsonMapperShortProduit", ObjectMapper.class);
// الرد
return response;
}
@RequestMapping(value = "/getLongProduitsById", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8")
public Response<List<Produit>> getLongProduitsById(HttpServletRequest request) {
// الأصل
Response<List<Produit>> response = super.getLongEntitiesById(request);
// مرشحات التسلسل jSON
context.getBean("jsonMapperLongProduit", ObjectMapper.class);
// الرد
return response;
}
@RequestMapping(value = "/getLongProduitsByName", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8")
public Response<List<Produit>> getLongProduitsByName(HttpServletRequest request) {
// الأصل
Response<List<Produit>> response = super.getLongEntitiesByName(request);
// مرشحات التسلسل jSON
context.getBean("jsonMapperLongProduit", ObjectMapper.class);
// الرد
return response;
}
@RequestMapping(value = "/saveProduits", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8")
public Response<List<CoreProduit>> saveProduits(HttpServletRequest request) {
...
}
@RequestMapping(value = "/deleteAllProduits", method = RequestMethod.GET)
public Response<Void> deleteAllProduits() {
return super.deleteAllEntities();
}
@RequestMapping(value = "/deleteProduitsById", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8")
public Response<Void> deleteProduitsById(HttpServletRequest request) {
return super.deleteEntitiesById(request);
}
@RequestMapping(value = "/deleteProduitsByName", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8")
public Response<Void> deleteProduitsByName(HttpServletRequest request) {
return super.deleteEntitiesByName(request);
}
}
تختلف طريقة [saveProduits] في بنيتها عن الطرق الأخرى:
@RequestMapping(value = "/saveProduits", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8")
public Response<List<CoreProduit>> saveProduits(HttpServletRequest request) {
try {
// يتم استرداد القيمة المرسلة
String body = CharStreams.toString(request.getReader());
// يتم إزالة التسلسل عنها
ObjectMapper mapper = context.getBean("jsonMapperShortProduit", ObjectMapper.class);
List<Produit> produits = mapper.readValue(body, new TypeReference<List<Produit>>() {
});
// يتم حفظ المنتجات
produits = daoProduit.saveEntities(produits);
List<CoreProduit> coreProduits = new ArrayList<CoreProduit>();
for (Produit produit : produits) {
coreProduits.add(new CoreProduit(produit.getId()));
}
// إرجاع الرد
return new Response<List<CoreProduit>>(0, null, coreProduits);
} catch (DaoException e) {
return new Response<List<CoreProduit>>(1, e.toString(), null);
} catch (Exception e) {
return new Response<List<CoreProduit>>(2, new ServerException(1021, e, simpleClassName).toString(), null);
}
}
- الأسطر 4-9: انطلاقًا من السلسلة jSON المستلمة، يتم إعادة بناء قائمة [Produit] المطلوب الاحتفاظ بها. ونظرًا لأن السلسلة jSON المستلمة هي الخاصة بالإصدارات المختصرة للمنتجات، فإن الحقل [categorie] لتلك الإصدارات يساوي null. مرة أخرى، لا تحتاج الطبقة DAO / JDBC إلى هذه المعلومات؛
- السطر 11: يتم حفظ المنتجات؛
- الأسطر 12-15: يتم إنشاء قائمة [CoreProduit] المطلوب إرجاعها؛
- السطر 18: يتم إرجاع الاستجابة التي سيتم تسلسلها (التسلسل الضمني الذي يقوم به Spring MVC) بواسطة أداة التعيين في السطر 7 قبل إرسالها إلى العميل البعيد (انظر مناقشة الفقرة 17.3.7)؛
17.3.9. فئة تنفيذ خدمة الويب / jSON
![]() |
الفئة [Boot] هي فئة التنفيذ للمشروع:
package spring.webjson.server.boot;
import org.springframework.boot.SpringApplication;
import spring.webjson.server.config.AppConfig;
public class Boot {
public static void main(String[] args) {
SpringApplication.run(AppConfig.class, args);
}
}
- السطر 10: يتم تنفيذ الطريقة الثابتة [SpringApplication.run]. فئة [SpringApplication] هي إحدى فئات مشروع [spring Boot] (السطر 3). يتم تمرير معلمتين إليها:
- [AppConfig.class]: الفئة التي تقوم بتكوين التطبيق بأكمله؛
- [args]: أي معلمات يتم تمريرها إلى الطريقة [main] في السطر 9. لا يتم استخدام هذه المعلمة هنا؛
عند تنفيذ هذه الفئة، نحصل على السجلات التالية:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.2.3.RELEASE)
11:34:08.661 [main] INFO spring.webjson.server.boot.Boot - Starting Boot on Gportpers3 with PID 6796 (started by ST in D:\data\istia-1415\spring data\dvp\dvp-spring-database-05\spring-database-generic\spring-webjson\spring-webjson-server-jdbc-generic)
11:34:08.700 [main] INFO o.s.b.c.e.AnnotationConfigEmbeddedWebApplicationContext - Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@2df32bf7: startup date [Mon Jun 08 11:34:08 CEST 2015]; root of context hierarchy
11:34:08.916 [main] INFO o.s.b.f.s.DefaultListableBeanFactory - Overriding bean definition for bean 'jsonMapper': replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=generic.jdbc.config.ConfigJdbc; factoryMethodName=jsonMapper; initMethodName=null; destroyMethodName=(inferred); defined in class generic.jdbc.config.ConfigJdbc] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=spring.webjson.server.config.WebConfig; factoryMethodName=jsonMapper; initMethodName=null; destroyMethodName=(inferred); defined in class spring.webjson.server.config.WebConfig]
11:34:08.917 [main] INFO o.s.b.f.s.DefaultListableBeanFactory - Overriding bean definition for bean 'jsonMapperLongCategorie': replacing [Root bean: class [null]; scope=prototype; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=generic.jdbc.config.ConfigJdbc; factoryMethodName=jsonMapperLongCategorie; initMethodName=null; destroyMethodName=(inferred); defined in class generic.jdbc.config.ConfigJdbc] with [Root bean: class [null]; scope=prototype; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=spring.webjson.server.config.WebConfig; factoryMethodName=jsonMapperLongCategorie; initMethodName=null; destroyMethodName=(inferred); defined in class spring.webjson.server.config.WebConfig]
11:34:08.918 [main] INFO o.s.b.f.s.DefaultListableBeanFactory - Overriding bean definition for bean 'jsonMapperShortProduit': replacing [Root bean: class [null]; scope=prototype; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=generic.jdbc.config.ConfigJdbc; factoryMethodName=jsonMapperShortProduit; initMethodName=null; destroyMethodName=(inferred); defined in class generic.jdbc.config.ConfigJdbc] with [Root bean: class [null]; scope=prototype; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=spring.webjson.server.config.WebConfig; factoryMethodName=jsonMapperShortProduit; initMethodName=null; destroyMethodName=(inferred); defined in class spring.webjson.server.config.WebConfig]
11:34:08.919 [main] INFO o.s.b.f.s.DefaultListableBeanFactory - Overriding bean definition for bean 'jsonMapperShortCategorie': replacing [Root bean: class [null]; scope=prototype; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=generic.jdbc.config.ConfigJdbc; factoryMethodName=jsonMapperShortCategorie; initMethodName=null; destroyMethodName=(inferred); defined in class generic.jdbc.config.ConfigJdbc] with [Root bean: class [null]; scope=prototype; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=spring.webjson.server.config.WebConfig; factoryMethodName=jsonMapperShortCategorie; initMethodName=null; destroyMethodName=(inferred); defined in class spring.webjson.server.config.WebConfig]
11:34:08.919 [main] INFO o.s.b.f.s.DefaultListableBeanFactory - Overriding bean definition for bean 'jsonMapperLongProduit': replacing [Root bean: class [null]; scope=prototype; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=generic.jdbc.config.ConfigJdbc; factoryMethodName=jsonMapperLongProduit; initMethodName=null; destroyMethodName=(inferred); defined in class generic.jdbc.config.ConfigJdbc] with [Root bean: class [null]; scope=prototype; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=spring.webjson.server.config.WebConfig; factoryMethodName=jsonMapperLongProduit; initMethodName=null; destroyMethodName=(inferred); defined in class spring.webjson.server.config.WebConfig]
11:34:09.409 [main] INFO o.s.b.c.e.t.TomcatEmbeddedServletContainer - Tomcat initialized with port(s): 8081 (http)
11:34:09.641 [main] INFO o.a.catalina.core.StandardService - Starting service Tomcat
11:34:09.642 [main] INFO o.a.catalina.core.StandardEngine - Starting Servlet Engine: Apache Tomcat/8.0.20
11:34:09.778 [localhost-startStop-1] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
11:34:09.778 [localhost-startStop-1] INFO o.s.web.context.ContextLoader - Root WebApplicationContext: initialization completed in 1081 ms
11:34:09.839 [localhost-startStop-1] INFO o.s.b.c.e.ServletRegistrationBean - Mapping servlet: 'dispatcherServlet' to [/*]
11:34:10.558 [main] INFO o.h.validator.internal.util.Version - HV000001: Hibernate Validator 5.1.3.Final
11:34:10.654 [main] INFO o.s.w.s.m.m.a.RequestMappingHandlerAdapter - Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@2df32bf7: startup date [Mon Jun 08 11:34:08 CEST 2015]; root of context hierarchy
11:34:10.745 [main] INFO o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped "{[/saveCategories],methods=[POST],params=[],headers=[],consumes=[application/json;charset=UTF-8],produces=[],custom=[]}" onto public spring.webjson.server.service.Response<java.util.List<spring.webjson.server.entities.CoreCategorie>> spring.webjson.server.service.CategorieController.saveCategories(javax.servlet.http.HttpServletRequest)
11:34:10.745 [main] INFO o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped "{[/deleteCategoriesById],methods=[POST],params=[],headers=[],consumes=[application/json;charset=UTF-8],produces=[],custom=[]}" onto public spring.webjson.server.service.Response<java.lang.Void> spring.webjson.server.service.CategorieController.deleteCategoriesById(javax.servlet.http.HttpServletRequest)
11:34:10.745 [main] INFO o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped "{[/getShortCategoriesByName],methods=[POST],params=[],headers=[],consumes=[application/json;charset=UTF-8],produces=[],custom=[]}" onto public spring.webjson.server.service.Response<java.util.List<spring.jdbc.entities.Categorie>> spring.webjson.server.service.CategorieController.getShortCategoriesByName(javax.servlet.http.HttpServletRequest)
11:34:10.746 [main] INFO o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped "{[/getAllLongCategories],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public spring.webjson.server.service.Response<java.util.List<spring.jdbc.entities.Categorie>> spring.webjson.server.service.CategorieController.getAllLongCategories()
11:34:10.746 [main] INFO o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped "{[/getLongCategoriesById],methods=[POST],params=[],headers=[],consumes=[application/json;charset=UTF-8],produces=[],custom=[]}" onto public spring.webjson.server.service.Response<java.util.List<spring.jdbc.entities.Categorie>> spring.webjson.server.service.CategorieController.getLongCategoriesById(javax.servlet.http.HttpServletRequest)
11:34:10.746 [main] INFO o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped "{[/deleteCategoriesByName],methods=[POST],params=[],headers=[],consumes=[application/json;charset=UTF-8],produces=[],custom=[]}" onto public spring.webjson.server.service.Response<java.lang.Void> spring.webjson.server.service.CategorieController.deleteCategoriesByName(javax.servlet.http.HttpServletRequest)
11:34:10.746 [main] INFO o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped "{[/getShortCategoriesById],methods=[POST],params=[],headers=[],consumes=[application/json;charset=UTF-8],produces=[],custom=[]}" onto public spring.webjson.server.service.Response<java.util.List<spring.jdbc.entities.Categorie>> spring.webjson.server.service.CategorieController.getShortCategoriesById(javax.servlet.http.HttpServletRequest)
11:34:10.746 [main] INFO o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped "{[/getAllShortCategories],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public spring.webjson.server.service.Response<java.util.List<spring.jdbc.entities.Categorie>> spring.webjson.server.service.CategorieController.getAllShortCategories()
11:34:10.747 [main] INFO o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped "{[/getLongCategoriesByName],methods=[POST],params=[],headers=[],consumes=[application/json;charset=UTF-8],produces=[],custom=[]}" onto public spring.webjson.server.service.Response<java.util.List<spring.jdbc.entities.Categorie>> spring.webjson.server.service.CategorieController.getLongCategoriesByName(javax.servlet.http.HttpServletRequest)
11:34:10.747 [main] INFO o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped "{[/deleteAllCategories],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public spring.webjson.server.service.Response<java.lang.Void> spring.webjson.server.service.CategorieController.deleteAllCategories()
11:34:10.748 [main] INFO o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped "{[/saveProduits],methods=[POST],params=[],headers=[],consumes=[application/json;charset=UTF-8],produces=[],custom=[]}" onto public spring.webjson.server.service.Response<java.util.List<spring.webjson.server.entities.CoreProduit>> spring.webjson.server.service.ProduitController.saveProduits(javax.servlet.http.HttpServletRequest)
11:34:10.749 [main] INFO o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped "{[/getShortProduitsById],methods=[POST],params=[],headers=[],consumes=[application/json;charset=UTF-8],produces=[],custom=[]}" onto public spring.webjson.server.service.Response<java.util.List<spring.jdbc.entities.Produit>> spring.webjson.server.service.ProduitController.getShortProduitsById(javax.servlet.http.HttpServletRequest)
11:34:10.749 [main] INFO o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped "{[/getAllLongProduits],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public spring.webjson.server.service.Response<java.util.List<spring.jdbc.entities.Produit>> spring.webjson.server.service.ProduitController.getAllLongProduits()
11:34:10.749 [main] INFO o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped "{[/getShortProduitsByName],methods=[POST],params=[],headers=[],consumes=[application/json;charset=UTF-8],produces=[],custom=[]}" onto public spring.webjson.server.service.Response<java.util.List<spring.jdbc.entities.Produit>> spring.webjson.server.service.ProduitController.getShortProduitsByName(javax.servlet.http.HttpServletRequest)
11:34:10.749 [main] INFO o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped "{[/getAllShortProduits],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public spring.webjson.server.service.Response<java.util.List<spring.jdbc.entities.Produit>> spring.webjson.server.service.ProduitController.getAllShortProduits()
11:34:10.749 [main] INFO o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped "{[/deleteProduitsByName],methods=[POST],params=[],headers=[],consumes=[application/json;charset=UTF-8],produces=[],custom=[]}" onto public spring.webjson.server.service.Response<java.lang.Void> spring.webjson.server.service.ProduitController.deleteProduitsByName(javax.servlet.http.HttpServletRequest)
11:34:10.750 [main] INFO o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped "{[/getLongProduitsByName],methods=[POST],params=[],headers=[],consumes=[application/json;charset=UTF-8],produces=[],custom=[]}" onto public spring.webjson.server.service.Response<java.util.List<spring.jdbc.entities.Produit>> spring.webjson.server.service.ProduitController.getLongProduitsByName(javax.servlet.http.HttpServletRequest)
11:34:10.750 [main] INFO o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped "{[/deleteProduitsById],methods=[POST],params=[],headers=[],consumes=[application/json;charset=UTF-8],produces=[],custom=[]}" onto public spring.webjson.server.service.Response<java.lang.Void> spring.webjson.server.service.ProduitController.deleteProduitsById(javax.servlet.http.HttpServletRequest)
11:34:10.750 [main] INFO o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped "{[/deleteAllProduits],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public spring.webjson.server.service.Response<java.lang.Void> spring.webjson.server.service.ProduitController.deleteAllProduits()
11:34:10.750 [main] INFO o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped "{[/getLongProduitsById],methods=[POST],params=[],headers=[],consumes=[application/json;charset=UTF-8],produces=[],custom=[]}" onto public spring.webjson.server.service.Response<java.util.List<spring.jdbc.entities.Produit>> spring.webjson.server.service.ProduitController.getLongProduitsById(javax.servlet.http.HttpServletRequest)
11:34:10.809 [main] INFO o.a.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8081"]
11:34:10.826 [main] INFO o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8081"]
11:34:10.860 [main] INFO o.a.tomcat.util.net.NioSelectorPool - Using a shared selector for servlet write/read
11:34:11.733 [main] INFO o.s.b.c.e.t.TomcatEmbeddedServletContainer - Tomcat started on port(s): 8081 (http)
11:34:11.934 [main] INFO spring.webjson.server.boot.Boot - Started Boot in 3.533 seconds (JVM running for 4.137)
11:34:20.382 [http-nio-8081-exec-1] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring FrameworkServlet 'dispatcherServlet'
11:34:20.384 [http-nio-8081-exec-1] INFO o.s.web.servlet.DispatcherServlet - FrameworkServlet 'dispatcherServlet': initialization started
11:34:20.410 [http-nio-8081-exec-1] INFO o.s.web.servlet.DispatcherServlet - FrameworkServlet 'dispatcherServlet': initialization completed in 26 ms
11:34:33.103 [http-nio-8081-exec-8] INFO o.s.b.f.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [org/springframework/jdbc/support/sql-error-codes.xml]
11:34:33.168 [http-nio-8081-exec-8] INFO o.s.j.support.SQLErrorCodesFactory - SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase, Hana]
- الأسطر 11-15: تم اكتشاف الحبوب التي تحدد المرشحات jSON. وهي تعيد تعريف الحبوب التي تحمل نفس الأسماء والتي تم اكتشافها في مشروع تكوين الطبقة JDBC؛
- الأسطر 17-18: بدء تشغيل خادم Tomcat الذي سيقوم بتنفيذ خدمة الويب / jSON؛
- السطور 19-21: يتم تهيئة سياق Spring MVC؛
- الأسطر 24-43: يتم اكتشاف الخدمات المعروضة URL؛
17.3.10. اختبارات خدمة الويب / jSON
لإجراء الاختبارات، نستخدم العميل [Advanced Rest Client] (انظر الفقرة 23.11) لاستعلام URL التي توفرها خدمة الويب / jSON (يجب تشغيل خدمة الويب / jSON بالطبع، وكذلك SGBD). ولملء قاعدة البيانات، نقوم بتنفيذ التكوين المسمى [spring-jdbc-generic-04-fillDataBase] الذي يملأ قاعدة البيانات بـ 5 فئات و10 منتجات:
![]() |
![]() |
- في [1-3]، نطلب URL و[/getAllLongCategories] عبر الأمر HTTP وGET؛
ونحصل على الرد التالي:
![]() |
- في [1]، الطلب HTTP من العميل؛
- في [2]، الرد HTTP من الخادم؛
- إلى [3]، حيث تشير الحالة [200 OK] إلى أن الخادم قد عالج الطلب بشكل صحيح؛
- في [4]، استجابة الخادم jSON؛
الرد الكامل jSON هو كما يلي:
{"status":0,"exception":null,"body":[{"id":1880,"version":1,"nom":"categorie[0]","produits":[{"id":9072,"version":1,"nom":"produit[0,0]","idCategorie":1880,"prix":100.0,"description":"desc[0,0]"},{"id":9073,"version":1,"nom":"produit[0,1]","idCategorie":1880,"prix":101.0,"description":"desc[0,1]"},{"id":9074,"version":1,"nom":"produit[0,2]","idCategorie":1880,"prix":102.0,"description":"desc[0,2]"},{"id":9075,"version":1,"nom":"produit[0,3]","idCategorie":1880,"prix":103.0,"description":"desc[0,3]"},{"id":9076,"version":1,"nom":"produit[0,4]","idCategorie":1880,"prix":104.0,"description":"desc[0,4]"}]},{"id":1881,"version":1,"nom":"categorie[1]","produits":[{"id":9077,"version":1,"nom":"produit[1,0]","idCategorie":1881,"prix":110.00000000000001,"description":"desc[1,0]"},{"id":9078,"version":1,"nom":"produit[1,1]","idCategorie":1881,"prix":111.00000000000001,"description":"desc[1,1]"},{"id":9079,"version":1,"nom":"produit[1,2]","idCategorie":1881,"prix":112.00000000000001,"description":"desc[1,2]"},{"id":9080,"version":1,"nom":"produit[1,3]","idCategorie":1881,"prix":112.99999999999999,"description":"desc[1,3]"},{"id":9081,"version":1,"nom":"produit[1,4]","idCategorie":1881,"prix":114.00000000000001,"description":"desc[1,4]"}]}]}
- status:0 يعني أنه لم تحدث أخطاء من جانب الخادم؛
- exception: null تعني أنه لا توجد رسالة خطأ؛
- body: هو نص الرد، وهو في هذه الحالة قائمة الفئات مع منتجاتها. توجد فئتان تحتوي كل منهما على 5 منتجات؛
سنقوم بإضافة المنتج [produit15] إلى الفئة [categorie1]. وللقيام بذلك، سنستخدم URL و[/saveProduits] اللذين ينتظران السلسلة jSON الخاصة بالمنتجات المراد الاحتفاظ بها (إدراج/تعديل). وستكون هذه السلسلة كما يلي:
[{"id":null,"version":null,"nom":"produit15","idCategorie":1881,"prix":111.0,"description":"desc15"}]}]
يتم إرسال الطلب إلى خدمة الويب / jSON بالطريقة التالية:
![]() |
- في [1]، يُطلب URL؛
- في [2]، يتم طلبها عبر عملية POST؛
- في [3]، تم إرسال السلسلة jSON؛
- في [4]، يتم إخطار الخادم بأنه سيتم إرسال jSON إليه؛
رد الخادم هو كما يلي:
![]() |
- في [1]، حصلنا على قائمة بـ [CoreProduit] مع مفاتيحها الأساسية. هنا، حصلنا على قائمة بعنصر واحد مع المفتاح الأساسي للمنتج الذي أدخلناه للتو في قاعدة البيانات؛
الآن، لنطلب النسخة المطولة للفئة المسماة [categorie[1]:
![]() |
- في [1]، URL المطلوب؛
- إلى [2]، نحولها إلى POST؛
- في [3,4]، القيمة التي تم إرسالها هي سلسلة jSON. وتمثل هذه السلسلة قائمة بأسماء الفئات التي نريد الحصول على نسختها الطويلة منها؛
ونحصل على النتيجة التالية:
![]() |
- في [5]، أصبحت الفئة [categorie[1]] تحتوي الآن على منتج سادس؛
لنحذف هذا المنتج الآن:
![]() |
- إلى [1]، والمنتج المطلوب URL؛
- إلى [2]، ونقوم بإنشاء POST؛
- في [3-4]، يتم إرسال سلسلة jSON التي تمثل قائمة المفاتيح الأساسية للمنتجات التي نريد حذفها؛
والنتيجة التي تم الحصول عليها هي كما يلي:
![]() |
- تشير [status:0] إلى أن عملية الحذف تمت بنجاح؛
الآن، لنطلب المنتج [produit[1,5]] للتحقق من أنه قد تم حذفه بالفعل:
![]() |
ونحصل على النتيجة التالية:
![]() |
- يشير [status:0] إلى أن العملية تمت دون أي استثناءات؛
- [body:[0]] تشير إلى أن [body] هي قائمة تحتوي على 0 عنصر. وبالتالي، فقد تم حذف الكيان [produit[1,5]] بالفعل؛
يمكن تنفيذ جميع عمليات [GET] في متصفح عادي:
![]() |
يُرجى من القارئ تجربة عمليات URL الأخرى الخاصة بخدمة الويب / jSON.








































