Skip to content

17. Bir veritabanını web üzerinde yayınlama

17.1. /jSON web hizmetinin mimarisi

Aşağıdaki mimariyi kuracağız:

  • [1]'te, [DAO, [JPA], JDBC] katmanları, önceki bölümlerde ve özellikle 15. paragrafta sunulan 24 yapılandırmadan biri ile uygulanır;
  • uzak istemcinin [DAO] [3] katmanı, [DAO] [1] katmanıyla aynı arayüzü uygular; bu da önceki bölümlerdeki ile aynı test katmanını kullanmamızı sağlar. Her şey, sanki [2-3] katmanları [4] katmanı için şeffafmış gibi gerçekleşir;

Aşağıdaki projelerden yararlanacağız:

  • altı SGBD projesinden birinin JDBC katmanını yapılandıran [sgbd-config-jdbc] projesi;
  • [sgbd-config-jpa-*] projesi, incelenen üç JPA uygulamasından biri için seçilen SGBD'in JPA katmanını yapılandırır (Hibernate, EclipseLink, OpenJpa);
  • [DAO] ve [1] katmanlarını uygulayan genel proje [spring-jdbc-04];
  • [DAO] ve [2] katmanlarını uygulayan [spring-jpa-generic] genel projesi;
  • [spring-jdbc-04] projesine dayanan bir web hizmetini uygulayan [spring-webjson-server-jdbc-generic] genel projesi;
  • [spring-jpa-generic] projesine dayanan bir web hizmetini uygulayan [spring-webjson-server-jpa-generic] genel projesi;
  • web hizmetinin 24 yapılandırmasının tek istemcisi olacak olan genel istemci [spring-webjson-client-generic];

17.2. Çalışma ortamının kurulması

Aşağıdaki öğelerle çalışacağız:

  • SGBD MySQL 5.6.25;
  • JPA Hibernate uygulaması;

Aşağıdaki projeleri STS'e içe aktarın:

  
  • [spring-webjson-*] projeleri, [<exemples>\spring-database-generic\spring-webjson] klasöründe bulunur;
  • [Alt-F5] komutunu çalıştırın ve ardından yukarıdaki tüm projeleri yeniden oluşturun;

Çalışma ortamının doğru şekilde kurulduğunu kontrol etmek için şu adımları izleyin:

  • [spring-webjson-server-jpa-generic-hibernate] çalıştırma yapılandırmasıyla web hizmetini başlatın; bu yapılandırma, JPA / Hibernate uygulamasına dayanmaktadır;

ardından:

  • [spring-webjson-client-generic] yürütme yapılandırmasıyla bu web hizmetinin istemcisini başlatın; bu yapılandırma, JUnit'in bir testidir:

Testin başarılı olması gerekir:

  • [1]'te, web hizmetini durdurun ve ardından JDBC uygulamasını temel alan [spring-webjson-server-jdbc-generic] yürütme yapılandırmasıyla web hizmetini başlatın:

ardından bu web hizmetinin istemcisini [spring-webjson-client-generic] yürütme yapılandırmasıyla başlatın:

Test başarılı olmalıdır:

 

17.3. Web hizmetinin uygulanması / jSON / JDBC

Öncelikle şu mimariye odaklanacağız:

burada [DAO] [1] katmanı, JDBC katmanıyla doğrudan iletişim kurar.

17.3.1. Web hizmetinin Eclipse projesi

Web hizmetinin Eclipse projesi / jSON / JDBC şu şekildedir:

  

Bu bir Maven projesidir ve [pom.xml] dosyası şu şekildedir:


<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>
        <!-- web katmanı -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- katman [DAO] -->
        <dependency>
            <groupId>dvp.spring.database</groupId>
            <artifactId>spring-jdbc-generic-04</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>
    <!-- eklentiler -->
    <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. satırlar: üst Maven projesi;
  • 24-28. satırlar: [spring-jdbc-generic-04] projesi tarafından uygulanan [DAO / JDBC] katmanına olan bağımlılık;
  • 19-22. satırlar: [spring-boot-starter-web] artefaktına olan bağımlılık. Bu artefakt, bir web hizmeti oluşturmak için gerekli tüm bağımlılıkları (jSON) beraberinde getirir. Ayrıca gereksiz kütüphaneler de içerir. Bu nedenle daha ayrıntılı bir yapılandırma gerekli olacaktır, ancak bu yapılandırma başlangıç için kullanışlıdır.

Bu yapılandırmanın getirdiği bağımlılıklar şunlardır:

1
  • [1] dosyasında, Eclipse'in [spring-jdbc-generic-04] proje arşivine olan bağımlılığı algıladığı görülmektedir;

Yukarıdaki bağımlılıklar hem [DAO] katmanına hem de [web] katmanına aittir.

17.3.2. [web] katmanının yapılandırılması

[web] katmanı, iki Spring yapılandırma dosyası ile yapılandırılır:

  

17.3.2.1. [WebConfig] sınıfı

[WebConfig] sınıfının temel işlevi şunlardır:

  • web hizmetinin dağıtılacağı Tomcat sunucusunu;
  • jSON nesnelerinin serileştirme/deserileştirme filtreleri [Produit] ve [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 {

    // -------------------------------- katman yapılandırması [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);
    }

    // -------------------------------- filtre yapılandırması [json]
    ...
}
  • 25. satır: sınıf, bir Spring yapılandırma sınıfıdır;
  • 26. satır: [@EnableWebMvc] anotasyonu, web katmanının Spring MVC ile uygulandığını belirtir. Bu, bizim yapmamız gerekmeyecek örtük yapılandırmalara yol açacaktır;
  • satır 30-31: uygulamanın Spring bağlamının enjeksiyonu;
  • satır 33-37: [dispatcherServlet] bean'inin tanımı; bu bean, Spring uygulamalarında MVC rolünü üstlenir ve görevleri, istemcilerin isteklerini bunları işleyebilecek denetleyiciye yönlendirmektir;
  • satır 39-42: Web hizmetinin servleti ve bu servletin işlediği URL'ler kaydedilir. Burada, tüm URLs'leri ifade eden [/*] yazılmıştır;
  • satır 44-47: Kullanılacak web sunucusunu tanımlayan [embeddedServletContainerFactory] bean'i tanımlanmıştır. Burada bu, Tomcat web sunucusu [http://tomcat.apache.org/] olacaktır. Jetty sunucusu [http://www.eclipse.org/jetty/] de kullanılabilir. Her ikisi de Maven bağımlılıklarında yerleşik sunuculardır. [Spring Boot] projeyi başlattığında, yapılandırmada belirtilen web sunucusunu otomatik olarak başlatır ve web hizmetini veya web uygulamasını bu sunucuya dağıtır;

jSON filtrelerinin yapılandırması şu şekilde yapılır:


package spring.webjson.server.config;

import java.util.List;
...

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    // -------------------------------- katman yapılandırması [web]
...
    // -------------------------------- filtre yapılandırması [json]
    // eşleme 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);
    }

    // filtreler 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. satır: [WebConfig] sınıfı, [WebMvcConfigurerAdapter] sınıfını genişletir. Bu son sınıf, web uygulamasını varsayılan değerlerle yapılandırır. Bu yapılandırmayı özelleştirmek istendiğinde, bu sınıfın belirli yöntemlerini yeniden tanımlamak gerekir. Burada, [22-26] satırındaki [configureMessageConverters] yöntemini yeniden tanımlamak istiyoruz ( @Override ekine dikkat edin); bu yöntem bir 'dönüştürücü' listesini tanımlar. /jSON web hizmeti ve istemcisi, metin satırları alışverişinde bulunur. Bir dönüştürücü, alınan bir metin satırından bir nesne oluşturabilen (deserializasyon) ve bir nesneden bir metin satırı oluşturabilen (serializasyon) bir araçtır. Burada metin satırları, jSON dizeleri olacaktır. Dolayısıyla jSON serileştirme/deserileştirme işleminden bahsedeceğiz;
  • 23. satır: [configureMessageConverters] yöntemi, parametre olarak bir dönüştürücü listesi alır;
  • 24-25. satırlar: [14-20] satırlarının dönüştürücüsü olan jSON [MappingJackson2HttpMessageConverter] bu listeye eklenir. Bu, istemci ile sunucu arasında jSON alışverişini mümkün kılacaktır;
  • [14-20] satırları: jSON dönüştürücüsünü tanımlar; bu dönüştürücü, [MappingJackson2HttpMessageConverter] sınıfı tarafından uygulanır. Bu sınıf (10. satır), projenin Maven bağımlılıkları arasında bulunur;
  • [17-18] satırları: bir jSON eşleştiricisi oluşturulur ve [MappingJackson2HttpMessageConverter] dönüştürücüsüne atanır;
  • [29-32] satırları: 17. satırda oluşturulan jSON eşleştiricisini bir Spring bean'i olarak tanımlar. Bu, onu Spring bağlamına yerleştirir ve diğer bean'lere enjekte edilebilmesi veya web uygulaması kodunda kullanılabilmesi için kullanılabilir hale getirir;
  • 34-41. satırlar: önceki jSON eşleştiricisi için bir jSON filtresi tanımlar;
  • 35. satır: [@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)] anotasyonu, burada tanımlanan bean'in bir singleton olmamasını sağlar. Bağlamdan her talep edildiğinde, [jsonMapperCategorieWithoutProduits] yöntemi yeniden çalıştırılır. Bu, burada dört adet jSON filtresi tanımladığımız için gereklidir. Ancak herhangi bir anda yalnızca bir tanesi aktif olmalıdır. Bean’e [ConfigurableBeanFactory.SCOPE_PROTOTYPE] kapsamını vererek, yöntemin yeniden çalıştırılacağından ve önceki filtrenin yenisiyle değiştirileceğinden emin oluruz;
  • Bu filtreleri anlamak için, [DAO] katmanında şunu hatırlamak gerekir:
    • [Produit] varlığı, [jsonFilterProduit] anotasyonu ile süslenmiştir;
    • [Categorie] varlığı, [jsonFilterCategorie] açıklamasıyla etiketlenmiştir;

Bu nedenle, bu isimleri taşıyan filtreler tanımlanmalıdır.

  • [34-41] satırları: [jsonMapperShortCategorie] adlı bir filtre tanımlar; bu filtre, bir kategorinin ürünlerini içermeyen jSON temsilini elde etmeyi sağlar;
  • [43-51] satırları: [jsonMapperLongCategorie] adlı bir filtre tanımlar; bu filtre, bir kategorinin ürünleriyle birlikte jSON temsilini elde etmeyi sağlar;
  • [53-60] satırları: [jsonMapperShortProduit] adlı bir filtre tanımlar; bu filtre, bir ürünün kategorisi hariç jSON temsilini elde etmeyi sağlar;
  • [62-70] satırları: [jsonMapperLongProduit] adlı bir filtre tanımlar; bu filtre, bir ürünün kategorisi ile birlikte jSON temsilini elde etmeyi sağlar;

17.3.2.2. [AppConfig] sınıfı

[AppConfig] sınıfı, uygulamanın tamamını, yani [web] ve [DAO] katmanlarını yapılandırır:


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. satır: sınıf, bir Spring yapılandırma sınıfıdır;
  • 9. satır: [DAO / JDBC] katmanındaki bean'ler ile [WebConfig] sınıfı tarafından tanımlanan bean'ler içe aktarılır. Böylece, [DAO] katmanındaki tüm bean'ler /jSON web uygulamasında kullanılabilir olacaktır;
  • 8. satır: Diğer Spring bean'lerinin hangi paketlerde bulunduğunu belirtir;

17.3.3. [ServerException] istisnası

  

Önceki bölümlerde olduğu gibi, [DAO] katmanı [DaoException] adlı kontrolsüz bir istisna oluşturuyordu; [web] katmanı ise [ServerException] adlı kontrolsüz bir istisna oluşturacaktır:


package spring.webjson.server.infrastructure;

import generic.jdbc.infrastructure.UncheckedException;

public class ServerException extends UncheckedException {

    private static final long serialVersionUID = 1L;

    // üreticiler
    public ServerException() {
        super();
    }

    public ServerException(int code, Throwable e, String simpleClassName) {
        super(code, e, simpleClassName);
    }
}
  • 5. satır: [ServerException] sınıfı, JDBC katmanını (3. satır) yapılandıran projede tanımlanan [UncheckedException] sınıfını genişletir;

17.3.4. Denetleyiciler

  

Burada iki denetleyici olacaktır:

  • [CategorieController], kategorilere yönelik istekleri kontrol edecektir;
  • [CategorieController], ürünlere yönelik istekleri kontrol edecektir;

Denetleyiciler tarafından sunulan URL'ler, [DAO] katmanındaki [DaoCategorie] ve [DaoProduit] arayüzlerinin yöntemleriyle bire bir eşleşir:

Yukarıdaki örnekte olduğu gibi:

  • [deleteAllCategories] web yöntemi, [DaoCategorie] sınıfındaki [deleteAllEntities] yöntemini çağıracaktır;
  • [getShortCategoriesById] web yöntemi, [DaoCategorie] sınıfındaki [getShortEntitiesById] yöntemini çağıracaktır;

Aynı durum ürünler için de geçerlidir:

17.3.4.1. [CategorieController] denetleyicisi tarafından sunulan URL

URL
Méthode

@RequestMapping(value = "/saveCategories",
 method = RequestMethod.POST, consumes =
 "application/json; charset=UTF-8")

public Response<List<CoreCategorie>>
 saveCategories
(HttpServletRequest request)
====
La méthode reçoit par un POST les catégories à persister. 
Celles-ci sont accessibles dans l'objet [HttpServletRequest
 request]. Les catégories sont persistées par la méthode 
[saveEntities] de la couche [DaoCategorie]. Seules les clés
 primaires des objets persistés (catégories / produits) sont
 renvoyées au client.

@RequestMapping(value = "/deleteAllCategories",
 method = RequestMethod.GET)

public Response<Void> deleteAllCategories()
====
L'URL n'a aucun paramètre. Les catégories sont supprimées par
 la méthode [deleteAllEntities] de la couche [DaoCategorie].

@RequestMapping(value = "/deleteCategoriesById",
 method = RequestMethod.POST, consumes =
 "application/json; charset=UTF-8")

public Response<Void> deleteCategoriesById
(HttpServletRequest request)
====
La méthode reçoit par un POST les clés primaires des
 catégories à supprimer. Celles-ci sont accessibles dans
 l'objet [HttpServletRequest request]. Les catégories sont
 supprimées par la méthode [deleteEntitiesById] de la couche
 [DaoCategorie].

@RequestMapping(value = "/deleteCategoriesByName",
 method = RequestMethod.POST, consumes =
 "application/json; charset=UTF-8")

public Response<Void> deleteCategoriesByName
(HttpServletRequest request)
====
La méthode reçoit par un POST les noms des catégories à 
supprimer. Ceux-ci sont accessibles dans l'objet
 [HttpServletRequest request]. Les catégories sont supprimées
 par la méthode [deleteEntitiesByname] de la couche 
[DaoCategorie].

@RequestMapping(value = "/getAllShortCategories",
 method = RequestMethod.GET)

public Response<List<Categorie>> getAllShortCategories()
====
L'URL n'a aucun paramètre. Les catégories courtes sont 
obtenues par la méthode [getAllShortEntities] de la couche
 [DaoCategorie].

@RequestMapping(value = "/getAllLongCategories",
 method = RequestMethod.GET)

public Response<List<Categorie>> getAllLongCategories()
====
L'URL n'a aucun paramètre. Les catégories longues sont
 obtenues par la méthode [getAllLongEntities] de la couche 
[DaoCategorie].

@RequestMapping(value = "/getLongCategoriesById",
 method = RequestMethod.POST)

public Response<List<Categorie>> getLongCategoriesById(HttpServletRequest request)
====
La méthode reçoit par un POST les clés primaires des
 catégories désirées. Celles-ci sont accessibles dans l'objet
 [HttpServletRequest request]. Les catégories longues sont
 obtenues par la méthode [getLongEntitiesById] de la couche
 [DaoCategorie].

@RequestMapping(value = "/getLongCategoriesByName",
 method = RequestMethod.POST)

public Response<List<Categorie>> getLongCategoriesByName
(HttpServletRequest request)
====
La méthode reçoit par un POST les noms des catégories
 désirées. Ceux-ci sont accessibles dans l'objet
 [HttpServletRequest request]. Les catégories longues sont 
obtenues par la méthode [getLongEntitiesByName] de la couche
 [DaoCategorie].

@RequestMapping(value = "/getShortCategoriesByName",
 method = RequestMethod.POST)

public Response<List<Categorie>> getShortCategoriesByName
(HttpServletRequest request)
====
La méthode reçoit par un POST les noms des catégories
 désirées. Ceux-ci sont accessibles dans l'objet
 [HttpServletRequest request]. Les catégories courtes sont
 obtenues par la méthode [getShortEntitiesByName] de la
 couche [DaoCategorie].

@RequestMapping(value = "/getShortCategoriesById",
 method = RequestMethod.POST)

public Response<List<Categorie>> getShortCategoriesById
(HttpServletRequest request)
====
La méthode reçoit par un POST les clés primaires des
 catégories désirées. Celles-ci sont accessibles dans l'objet
 [HttpServletRequest request]. Les catégories courtes sont
 obtenues par la méthode [getShortEntitiesById] de la couche
 [DaoCategorie].

17.3.4.2. [ProduitController] denetleyicisi tarafından sunulan URL

URL
Méthode

@RequestMapping(value = "/saveProduits", method =
 RequestMethod.POST, consumes = "application/json;
 charset=UTF-8")

public Response<List<CoreProduit>> saveProduits
(HttpServletRequest request)
====
La méthode reçoit les produits à persister par un POST. Ceux-
ci sont accessibles dans l'objet [HttpServletRequest
 request]. Les produits sont persistés par la méthode
 [saveEntities] de la couche [DaoProduit]. Seules les clés
 primaires des produits sont renvoyées au client.

@RequestMapping(value = "/deleteAllProduits",
 method = RequestMethod.GET)

public Response<Void> deleteAllProduits()
====
L'URL n'a aucun paramètre. Les produits sont supprimés par la
 méthode [deleteAllEntities] de la couche [DaoProduit].

@RequestMapping(value = "/deleteProduitsById",
 method = RequestMethod.POST, consumes =
 "application/json; charset=UTF-8")

public Response<Void> deleteProduitsById
(HttpServletRequest request)
====
La méthode reçoit par un POST les clés primaires des produits
 à supprimer. Celles-ci sont accessibles dans l'objet
 [HttpServletRequest request]. Les produits sont supprimés
 par la méthode [deleteEntitiesById] de la couche 
[DaoProduit].

@RequestMapping(value = "/deleteProduitsByName",
 method = RequestMethod.POST, consumes =
 "application/json; charset=UTF-8")

public Response<Void> deleteProduitsByName
(HttpServletRequest request)
====
La méthode reçoit par un POST les noms des produits à
 supprimer. Ceux-ci sont accessibles dans l'objet
 [HttpServletRequest request]. Les produits sont supprimés
 par la méthode [deleteEntitiesByname] de la couche
 [DaoProduit].

@RequestMapping(value = "/getAllShortProduits",
 method = RequestMethod.GET)

public Response<List<Produit>> getAllShortProduits()
====
L'URL n'a aucun paramètre. Les catégories courtes sont
 obtenues par la méthode [getAllShortEntities] de la couche
 [DaoProduit].

@RequestMapping(value = "/getAllLongProduits",
 method = RequestMethod.GET)

public Response<List<Produit>> getAllLongProduits()
====
L'URL n'a aucun paramètre. Les produits longs sont obtenus
 par la méthode [getAllLongEntities] de la couche
 [DaoProduit].

@RequestMapping(value = "/getLongProduitsById",
 method = RequestMethod.POST)

public Response<List<Produit>> getLongProduitsById
(HttpServletRequest request)
====
La méthode reçoit par un POST les clés primaires des produits
 désirés. Celles-ci sont accessibles dans l'objet
 [HttpServletRequest request]. Les produits longs sont 
obtenus par la méthode [getLongEntitiesById] de la couche

 [DaoProduit].

@RequestMapping(value = "/getLongProduitsByName",
 method = RequestMethod.POST)

public Response<List<Produit>> getLongProduitsByName
(HttpServletRequest request)
====
La méthode reçoit par un POST les noms des produits désirés.
 Ceux-ci sont accessibles dans l'objet [HttpServletRequest
 request]. Les produits longs sont obtenus par la méthode
 [getLongEntitiesByName] de la couche [DaoProduit].

@RequestMapping(value = "/getShortProduitsByName",
 method = RequestMethod.POST)

public Response<List<Produit>> getShortProduitsByName
(HttpServletRequest request)
====
La méthode reçoit par un POST les noms des produits désirés.
 Ceux-ci sont accessibles dans l'objet [HttpServletRequest
 request]. Les produits courts sont obtenus par la méthode 
[getShortEntitiesByName] de la couche [DaoProduit].

@RequestMapping(value = "/getShortProduitsById",
 method = RequestMethod.POST)

public Response<List<Produit>> getShortProduitsById
(HttpServletRequest request)
====
La méthode reçoit par un POST les clés primaires des produits
 désirés. Celles-ci sont accessibles dans l'objet 
[HttpServletRequest request]. Les produits courts sont 
obtenus par la méthode [getShortEntitiesById] de la couche
 [DaoProduit].

17.3.5. Web hizmetinin genel uygulaması

  

Web hizmeti tarafından sunulan URL listesi, kategorileri ve ürünleri yönetmek için aynı türde URL'lerin sunulduğunu göstermektedir. Birbirine çok benzeyen iki denetleyici yazmak yerine, her iki denetleyicide ortak olan tüm işleri halledecek bir sınıftan türetilmesini sağlayacağız. Bu, yukarıdaki [AbstractController] sınıfı olacaktır. Bu sınıf, aşağıdaki [Iws] arayüzünü uygulayacaktır:


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üm T varlıklarının listesi
    public Response<List<T>> getAllShortEntities();

    public Response<List<T>> getAllLongEntities();

    // belirli varlıklar - kısa sürüm
    public Response<List<T>> getShortEntitiesById(HttpServletRequest request);

    public Response<List<T>> getShortEntitiesByName(HttpServletRequest request);

    // belirli varlıklar - uzun sürüm
    public Response<List<T>> getLongEntitiesById(HttpServletRequest request);

    public Response<List<T>> getLongEntitiesByName(HttpServletRequest request);

    // birden fazla viten güncellemesi
    public Response<List<T>> saveEntities(HttpServletRequest request);

    // tüm varlıkların silinmesi
    public  Response<Void> deleteAllEntities();

    // birden fazla varlığın silinmesi
    public  Response<Void> deleteEntitiesById(HttpServletRequest request);

    public  Response<Void> deleteEntitiesByName(HttpServletRequest request);
}

Bu arayüz, kullanılacak olan [DAO] katmanının arayüzündeki yöntemleri içerir:


package spring.jdbc.dao;

import java.util.List;

import spring.jdbc.entities.AbstractCoreEntity;

public interface IDao<T extends AbstractCoreEntity> {

    // tüm T varlıklarının listesi
    public List<T> getAllShortEntities();

    public List<T> getAllLongEntities();

    // belirli varlıkların listesi - kısa sürüm
    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);

    // belirli varlıklar - uzun sürüm
    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);

    // birden fazla varlığın güncellenmesi
    public List<T> saveEntities(Iterable<T> entities);

    public List<T> saveEntities(@SuppressWarnings("unchecked") T... entities);

    // tüm varlıkların silinmesi
    public void deleteAllEntities();

    // birden fazla varlığın silinmesi
    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);
}

[DAO] katmanındaki [IDao<T>] arayüzünden web hizmetinin [Iws<T>] arayüzüne geçiş, aşağıdaki kurallara uygun olarak gerçekleştirilmiştir:

  • [Iws<T>] arayüzündeki yöntemler istisna oluşturmayacaktır. Bir istisna oluşursa, bu istisna [Response] nesnesinde kapsüllenecektir;
  • 45. ve 47. satırlardaki [Iterable<String> names, String... names] gibi parametre varyantları ortadan kalkar. Yöntemler, parametrelerini [HttpServletRequest request] türündeki istemcinin HTTP isteğinden alır;

Web hizmetinin tüm yanıtları aşağıdaki [Response] nesnesine kapsüllenecektir:

  

package spring.webjson.server.service;


public class Response<T> {

    // ----------------- özellikler
    // işlem durumu
    private int status;
    // bir hata mesajı
    private String exception;
    // yanıt gövdesi
    private T body;

    // oluşturucular
    public Response() {

    }

    public Response(int status, String exception, T body) {
        this.status = status;
        this.exception = exception;
        this.body = body;
    }

    // alıcı ve ayarlayıcılar
...
}
  • 4. satır: yanıt, T türünü içerir;
  • 12. satır: T türündeki yanıt;
  • 7-10. satırlar: Bir yöntem bir istisna ile karşılaşabilir. Bu durumda, aşağıdaki şekilde bir yanıt döndürür:
    • satır 8: status!=0;
    • satır 10: bir hata mesajı;

[AbstractController] sınıfı şu şekildedir:


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;

    // katman DAO
    private IDao<T> dao;

    abstract protected IDao<T> getDao();

    // yerel
    private String simpleClassName = getClass().getSimpleName();

    @PostConstruct
    public void init(){
        dao=getDao();
    }
    
    @Override
    public Response<List<T>> getAllShortEntities() {
        try {
            // yanıt
            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 {
            // yanıt
            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 {
            // siliniyor
            dao.deleteAllEntities();
            // yanıt
            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) {
    ...
    }

}

Tüm yöntemler aynı şekilde uygulanır:

  1. eğer bilgi bekliyorlarsa, bu bilgileri [HttpServletRequest request] nesnesinden alırlar;
  2. [DAO] katmanındaki, kendileriyle aynı adı taşıyan yöntemi çağırırlar;
  3. parametrelerin alınmasına ilişkin 1. işlemde veya [DAO] katmanına yapılan çağrıya ilişkin 2. işlemde meydana gelebilecek istisnaları yönetirler;

Öncelikle, [DAO] katmanının [AbstractController] sınıfına nasıl enjekte edildiğine bir göz atalım:


public abstract class AbstractController<T extends AbstractCoreEntity> implements Iws<T> {

    @Autowired
    protected ApplicationContext context;

    // katman DAO
    private IDao<T> dao;

    abstract protected IDao<T> getDao();

    @PostConstruct
    public void init(){
        dao=getDao();
}

  • 1. satır: Sınıf soyuttur ve [Iws<T>] genel arayüzünü uygular;
  • 3-4. satırlar: Spring bağlamının enjeksiyonu;
  • 7. satır: Kullanılacak [DAO] katmanının henüz bilinmeyen referansı;
  • 9. satır: Kullanılacak [DAO] katmanının referansını döndürecek olan [getDao] soyut yöntemi. Bu yöntem alt sınıf tarafından yeniden tanımlanacak ve dolayısıyla hangi [DAO] katmanının (DaoProduit mi yoksa DaoCategorie mi) kullanılacağını belirleyecek olan alt sınıftır;
  • 11. satır: [@PostConstruct] anotasyonu, nesnenin örneklenmesi tamamlandığında çalıştırılacak bir yöntemi belirtir. Bu örneklenme tamamlandığında, Spring enjeksiyonları gerçekleştirilmiş olur. Böylece alt sınıf, [DAO] katmanının referansını almış olacak ve bunu üst sınıfına iletebilecektir;

[getShortEntitiesById] yöntemi şu şekildedir:


    @Override
    public Response<List<T>> getShortEntitiesById(HttpServletRequest request) {
        try {
            // gönderilen değer alınır
            String body = CharStreams.toString(request.getReader());
            // serileştirme işlemi
            ObjectMapper mapper = context.getBean("jsonMapper", ObjectMapper.class);
            List<Long> ids = mapper.readValue(body, new TypeReference<List<Long>>() {
            });
            // yanıt
            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. satır: İstemci tarafından gönderilen değer, jSON dizesi olacaktır. Bu değer burada alınır;
  • 7-9. satırlar: jSON dizesi, kısa sürümünü almak istediğimiz varlıkların birincil anahtarlarının listesini içerir;
  • 11. satır: Aynı isimdeki [DAO] yöntemi çağrılır. [List<T>] türündeki yanıt, bir [Response] nesnesine kapsüllenir;
  • 13. satır: [DAO] katmanının bir istisna oluşturduğu durum;
  • satır 15: diğer istisnalar için, özellikle de 8. satırdaki jSON parametresinin serileştirilmesinden kaynaklanabilecek istisna;

[getShortEntitiesByName] yöntemi de benzerdir:


@Override
    public Response<List<T>> getShortEntitiesByName(HttpServletRequest request) {
        try {
            // gönderilen değer alınır
            String body = CharStreams.toString(request.getReader());
            // de-serialize ediliyor
            ObjectMapper mapper = context.getBean("jsonMapper", ObjectMapper.class);
            List<String> noms = mapper.readValue(body, new TypeReference<List<String>>() {
            });
            // yanıt
            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);
        }
}
  • satır 4-9: burada jSON parametresi, kısa versiyonunu almak istediğimiz kategori adlarının listesidir;

[saveEntities] yöntemi, kalıcı hale getirilecek varlığın türüne ([Categorie] veya [Produit]) oldukça bağlı olduğu için uygulanmamıştır. Faktörleştirilecek çok az kod vardır. Dolayısıyla bu iş, alt sınıflara bırakılmıştır.


        @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] denetleyicisi

  

[CategorieController] denetleyicisi, aşağıdaki kategorilerle ilgili URL işlemlerini yönetir:


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

    // yerel
    private String simpleClassName = getClass().getSimpleName();

    @RequestMapping(value = "/getAllShortCategories", method = RequestMethod.GET)
    public Response<List<Categorie>> getAllShortCategories() {
        // üst öğe
        Response<List<Categorie>> response = super.getAllShortEntities();
        // serileştirme filtreleri jSON
        context.getBean("jsonMapperShortCategorie", ObjectMapper.class);
        // yanıt
        return response;
    }

    @RequestMapping(value = "/getAllLongCategories", method = RequestMethod.GET)
    public Response<List<Categorie>> getAllLongCategories() {
        // üst öğe
        Response<List<Categorie>> response = super.getAllLongEntities();
        // serileştirme filtreleri jSON
        context.getBean("jsonMapperLongCategorie", ObjectMapper.class);
        // yanıt
        return response;
    }

    @RequestMapping(value = "/getShortCategoriesById", method = RequestMethod.POST)
    public Response<List<Categorie>> getShortCategoriesById(HttpServletRequest request) {
        // üst öğe
        Response<List<Categorie>> response = super.getShortEntitiesById(request);
        // serileştirme filtreleri jSON
        context.getBean("jsonMapperShortCategorie", ObjectMapper.class);
        // yanıt
        return response;
    }

    @RequestMapping(value = "/getShortCategoriesByName", method = RequestMethod.POST)
    public Response<List<Categorie>> getShortCategoriesByName(HttpServletRequest request) {
        // üst öğe
        Response<List<Categorie>> response = super.getShortEntitiesByName(request);
        // serileştirme filtreleri jSON
        context.getBean("jsonMapperShortCategorie", ObjectMapper.class);
        // yanıt
        return response;
    }

    @RequestMapping(value = "/getLongCategoriesById", method = RequestMethod.POST)
    public Response<List<Categorie>> getLongCategoriesById(HttpServletRequest request) {
        // üst öğe
        Response<List<Categorie>> response = super.getLongEntitiesById(request);
        // serileştirme filtreleri jSON
        context.getBean("jsonMapperLongCategorie", ObjectMapper.class);
        // yanıt
        return response;
    }

    @RequestMapping(value = "/getLongCategoriesByName", method = RequestMethod.POST)
    public Response<List<Categorie>> getLongCategoriesByName(HttpServletRequest request) {
        // üst öğe
        Response<List<Categorie>> response = super.getLongEntitiesByName(request);
        // serileştirme filtreleri jSON
        context.getBean("jsonMapperLongCategorie", ObjectMapper.class);
        // yanıt
        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. satır: [CategorieController] sınıfı, [AbstractController] sınıfını genişletir;
  • satır 25: [@RestController] anotasyonu, sınıfı bir Spring bileşeni haline getirir. Bu anotasyon ayrıca, sınıfın yöntemlerinin yanıtlarını doğrudan jSON biçiminde istemciye gönderen bir web hizmeti olduğunu belirtir;
  • 28-29. satırlar: [DAO] katman referansı buraya enjekte edilir;
  • satır 31-34: üst sınıfta soyut olarak bildirilen ve amacı kullanılacak [DAO] katmanına bir referans döndürmek olan [getDao] yönteminin yeniden tanımlanması;

Tüm yöntemler aynı modele göre oluşturulmuştur:

  • işlemin üst sınıfa devredilmesi;
  • yanıtın serileştirilmesini gerçekleştirecek olan jSON eşleştiricisinin başlatılması;
  • yanıtın gönderilmesi;

Şimdi birkaç URL'in imzasına bakalım:


@RequestMapping(value = "/getAllShortCategories", method
 = RequestMethod.GET)
- URL [/getAllShortCategories], bir GET ile çağrılır

@RequestMapping(value = "/getShortCategoriesById",
 method = RequestMethod.POST, consumes =
 "application/json; charset=UTF-8")
- URL [/getShortCategoriesById], bir POST ile çağrılır. Kaydedilen değer, istenen kategorilerin birincil anahtarlarından oluşan jSON dizesidir
istenen kategorilerin birincil anahtarlarından oluşan jSON dizesidir;

@RequestMapping(value = "/getLongCategoriesByName",
 method = RequestMethod.POST, consumes =
 "application/json; charset=UTF-8")
- URL ve [/getLongCategoriesByName],
POST ile çağrılır. Gönderilen değer, istenen kategorilerin adlarından oluşan jSON dizesidir
;

Şimdi, diğer yöntemlerin formatına uymayan [saveCategories] yöntemini ayrıntılı olarak inceleyelim:


    @RequestMapping(value = "/saveCategories", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8")
    public Response<List<CoreCategorie>> saveCategories(HttpServletRequest request) {
        // kategoriler kalıcı hale getirilir
        try {
            // gönderilen değer alınır
            String body = CharStreams.toString(request.getReader());
            // serileştirmeyi kaldırma
            ObjectMapper mapper = context.getBean("jsonMapperLongCategorie", ObjectMapper.class);
            List<Categorie> categories = mapper.readValue(body, new TypeReference<List<Categorie>>() {
            });
            // kategoriler korunur
            categories = daoCategorie.saveEntities(categories);
            // sonuç döndürülüyor
            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);
                }
            }
            // sonuç
            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. satır: URL [/saveCategories]'e bir gönderilen değer eşlik eder. Bu değer, kalıcı hale getirilecek kategorilerin uzun versiyonlarından oluşan jSON dizesidir;
  • 5-10. satırlar: Korumak istenen kategoriler, jSON dizesinden yeniden oluşturulur. Bir [Produit]'i kendi [Catégorie]'ine bağlayan [produit.categorie] bağlantısının değeri null'tir; çünkü bir [Categorie]'in uzun versiyonunda, her bir [Produit], [categorie] alanı olmadan kendi kısa versiyonundadır. Bu bir sorun teşkil etmez, çünkü JDBC ile uygulanan [DAO] katmanı bu bilgiye ihtiyaç duymaz;
  • 12. satır: kategoriler kalıcı hale getirilir. Alınan kategori listesi, kalıcı hale getirilen öğelerin (kategoriler ve ürünler) birincil anahtarlarıyla zenginleştirilmiştir. Başka hiçbir şey değişmemiştir. Maliyetli olan alınan listenin tamamını döndürmek yerine, bu listedeki öğelerin yalnızca birincil anahtarlarını döndüreceğiz. Bunun için aşağıdaki [CoreCategorie] ve [CoreProduit] sınıfları kullanılır:
  

package spring.webjson.server.entities;

import java.util.List;

public class CoreCategorie {

    // birincil anahtar
    private Long id;
    
    // üreticiler
    public CoreCategorie() {

    }

    public CoreCategorie(Long id) {
        this.id=id;
    }

    // ürün listesi
    private List<CoreProduit> coreProduits;

    // alıcı ve ayarlayıcılar
    ...
}
  • 8. satır: bir ürünün birincil anahtarı;
  • 20. satır: ürünlerin birincil anahtarları;

package spring.webjson.server.entities;

public class CoreProduit {

    // birincil anahtar
    private Long id;

    // oluşturucular
    public CoreProduit() {

    }

    public CoreProduit(Long id) {
        this.id = id;
    }

    // getter ve setter'lar
...
}
  • 6. satır: bir ürünün birincil anahtarı;

[saveCategories] yönteminin koduna geri dönelim:


...            
// kategorileri kalıcı hale getiriyoruz
            categories = daoCategorie.saveEntities(categories);
            // sonuç döndürülür
            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);
                }
            }
            // sonuç
            return new Response<List<CoreCategorie>>(0, null, coreCategories);
...
  • satır 5-17: uzak müşteriye döndürülecek [CoreCategorie] listesi oluşturulur;
  • 19. satır: yanıt gönderilir ve jSON olarak serileştirilir;

17.3.7. jSON filtrelerinin yönetimi

Bir denetleyicinin her yöntemi için, jSON serileştirme/serileştirme kaldırma işlemleri iki aşamada gerçekleşir:

  • gönderilen değerin serileştirilmesinin kaldırılması: bu işlem burada açıkça yönetilmektedir;
  • sonucun serileştirilmesi: burada örtük olarak yönetilir;

Öncelikle [CategorieController.saveCategories]'e gönderilen değerin deserializasyonuyla başlayalım:


    @RequestMapping(value = "/saveCategories", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8")
    public Response<List<CoreCategorie>> saveCategories(HttpServletRequest request) {
        // kategorileri kalıcı hale getirme
        try {
            // gönderilen değeri alıyoruz
            String body = CharStreams.toString(request.getReader());
            // serileştirmeyi kaldırıyoruz
            ObjectMapper mapper = context.getBean("jsonMapperLongCategorie", ObjectMapper.class);
            List<Categorie> categories = mapper.readValue(body, new TypeReference<List<Categorie>>() {
});
  • 8. satır: Spring bağlamında, jSON [jsonMapperLongCategorie] filtresini yönetmek üzere yapılandırılmış bir eşleştirici alınır. [WebConfig] yapılandırma sınıfındaki bu eşleştiricinin tanımına geri dönelim:

// -------------------------------- filtre yapılandırması [json]
    // eşleme 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);
    }

    // filtreler 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. satırlar: [CategorieController] sınıfı tarafından alınan jSON [jsonMapperLongCategorie] eşleştiricisi;
  • satır 35: bu eşleştirici, 18-21. satırlardaki [jsonMapper] yöntemi tarafından oluşturulur;
  • satır 18-21: [jsonMapper] yöntemi, satır 3-9'daki [MappingJackson2HttpMessageConverter] dönüştürücüsünün jSON eşleştiricisini döndürür;

Diğer bir deyişle, aşağıdaki 4. satırda [CategorieController.saveCategories]'ten alınan jSON eşleştiricisi:


            // gönderilen değer alınır
            String body = CharStreams.toString(request.getReader());
            // serileştirme işlemi kaldırılır
            ObjectMapper mapper = context.getBean("jsonMapperLongCategorie", ObjectMapper.class);
            List<Categorie> categories = mapper.readValue(body, new TypeReference<List<Categorie>>() {
});

, Spring tarafından varsayılan olarak kullanılan dönüştürücüdür. MVC, istemci tarafından gönderilen değeri deserialize etmek ve kendisine gönderilen sonucu serialize etmek için kullanılır. Yukarıdaki satırlarda, gönderilen değerin örtük bir deserializasyonu gerçekleşmemiştir. Bunun için şunu yazmak gerekirdi:


    @RequestMapping(value = "/saveCategories", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8")
    public Response<List<CoreCategorie>> saveCategories(@RequestBody List<Categorie> categories) {

Bu durumda, [categories] parametresinde gönderilen değerin otomatik olarak deserializasyonu gerçekleşirdi. Ancak, [Categorie] varlıklarının sahip olduğu [jsonFilterCategorie] filtresiyle ilgili bir sorun vardı. Bu filtrenin yapılandırılması gerekiyordu. Bu nedenle, açık bir deserializasyon seçtik (4-5. satırlar). Dikkat edilmesi gereken ikinci nokta, 4. satırdaki eşleştiricinin (Spring tarafından varsayılan olarak kullanılan MVC) [Response<List<CoreCategorie>] sonucunun serileştirilmesi için de uygun olduğudur. Nitekim, [CoreCategorie] varlığında jSON filtresi bulunmamaktadır. Dolayısıyla, elde edilen jSON eşleştiricisini ek bir filtre ile yapılandırmaya gerek yoktur. Bu durumda, müşteriye gönderilen yanıtın örtük bir serileştirmesi yapılacaktır.

17.3.8. [ProduitController] denetleyicisi

  

[ProduitController] denetleyicisi, ürünlerle ilgili URL işlemlerini yönetir. Kodu, [CategorieController] denetleyicisinin koduna benzerdir:


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

    // yerel
    private String simpleClassName = getClass().getSimpleName();

    @RequestMapping(value = "/getAllShortProduits", method = RequestMethod.GET)
    public Response<List<Produit>> getAllShortProduits() {
        // üst öğe
        Response<List<Produit>> response = super.getAllShortEntities();
        // serileştirme filtreleri jSON
        context.getBean("jsonMapperShortProduit", ObjectMapper.class);
        // yanıt
        return response;
    }

    @RequestMapping(value = "/getAllLongProduits", method = RequestMethod.GET)
    public Response<List<Produit>> getAllLongProduits() {
        // üst öğe
        Response<List<Produit>> response = super.getAllLongEntities();
        // serileştirme filtreleri jSON
        context.getBean("jsonMapperLongProduit", ObjectMapper.class);
        // yanıt
        return response;
    }

    @RequestMapping(value = "/getShortProduitsById", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8")
    public Response<List<Produit>> getShortProduitsById(HttpServletRequest request) {
        // üst öğe
        Response<List<Produit>> response = super.getShortEntitiesById(request);
        // serileştirme filtreleri jSON
        context.getBean("jsonMapperShortProduit", ObjectMapper.class);
        // yanıt
        return response;
    }

    @RequestMapping(value = "/getShortProduitsByName", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8")
    public Response<List<Produit>> getShortProduitsByName(HttpServletRequest request) {
        // üst öğe
        Response<List<Produit>> response = super.getShortEntitiesByName(request);
        // serileştirme filtreleri jSON
        context.getBean("jsonMapperShortProduit", ObjectMapper.class);
        // yanıt
        return response;
    }

    @RequestMapping(value = "/getLongProduitsById", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8")
    public Response<List<Produit>> getLongProduitsById(HttpServletRequest request) {
        // üst öğe
        Response<List<Produit>> response = super.getLongEntitiesById(request);
        // serileştirme filtreleri jSON
        context.getBean("jsonMapperLongProduit", ObjectMapper.class);
        // yanıt
        return response;
    }

    @RequestMapping(value = "/getLongProduitsByName", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8")
    public Response<List<Produit>> getLongProduitsByName(HttpServletRequest request) {
        // üst öğe
        Response<List<Produit>> response = super.getLongEntitiesByName(request);
        // serileştirme filtreleri jSON
        context.getBean("jsonMapperLongProduit", ObjectMapper.class);
        // yanıt
        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);
    }

}

Yalnızca [saveProduits] yöntemi, diğer yöntemlerden farklı bir yapıya sahiptir:


@RequestMapping(value = "/saveProduits", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8")
    public Response<List<CoreProduit>> saveProduits(HttpServletRequest request) {
        try {
            // gönderilen değer alınır
            String body = CharStreams.toString(request.getReader());
            // serileştirme işlemi kaldırılıyor
            ObjectMapper mapper = context.getBean("jsonMapperShortProduit", ObjectMapper.class);
            List<Produit> produits = mapper.readValue(body, new TypeReference<List<Produit>>() {
            });
            // ürünler kalıcı hale getiriliyor
            produits = daoProduit.saveEntities(produits);
            List<CoreProduit> coreProduits = new ArrayList<CoreProduit>();
            for (Produit produit : produits) {
                coreProduits.add(new CoreProduit(produit.getId()));
            }
            // yanıt verilir
            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. satırlar: Alınan jSON dizesinden, kalıcı hale getirilecek [Produit] listesi yeniden oluşturulur. Alınan jSON dizesi ürünlerin kısa sürümlerine ait olduğundan, bunların [categorie] alanı null değerindedir. Yine, DAO / JDBC katmanının bu bilgiye ihtiyacı yoktur;
  • 11. satır: ürünler kalıcı hale getirilir;
  • 12-15. satırlar: döndürülecek [CoreProduit] listesi oluşturulur;
  • 18. satır: Yanıt, uzak istemciye gönderilmeden önce 7. satırdaki eşleştirici tarafından serileştirilir (Spring tarafından gerçekleştirilen örtük serileştirme MVC) (bkz. 17.3.7. paragrafındaki tartışma);

17.3.9. Web hizmetinin yürütme sınıfı / jSON

  

[Boot] sınıfı, projenin yürütülebilir sınıfıdır:


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. satır: [SpringApplication.run] statik yöntemi yürütülür. [SpringApplication] sınıfı, [spring Boot] projesinin bir sınıfıdır (3. satır). Bu sınıfa iki parametre aktarılır:
    • [AppConfig.class]: Uygulamanın tamamını yapılandıran sınıf;
    • [args]: 9. satırdaki [main] yöntemine aktarılan olası argümanlar. Bu parametre burada kullanılmamaktadır;

Bu sınıf çalıştırıldığında aşağıdaki günlük kayıtları elde edilir:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: 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. satırlar: jSON filtrelerini tanımlayan bean'ler tespit edilir. Bunlar, JDBC katmanının yapılandırma projesinde tespit edilen aynı isimli bean'leri yeniden tanımlar;
  • 17-18. satırlar: / jSON web hizmetini çalıştıracak olan Tomcat sunucusu başlatılıyor;
  • satır 19-21: MVC Spring bağlamı başlatılır;
  • satır 24-43: Açığa çıkarılan URL'ler tespit edilir;

17.3.10. / jSON web hizmetinin testleri

Testleri yapmak için, / jSON web hizmeti tarafından sunulan URL'leri sorgulamak üzere [Advanced Rest Client] istemcisini (bkz. paragraf 23.11) kullanıyoruz (tabii ki / jSON web hizmeti ve SGBD de başlatılmalıdır). Veritabanını doldurmak için, veritabanını 5 kategori ve 10 ürünle dolduran [spring-jdbc-generic-04-fillDataBase] adlı yürütme yapılandırmasını çalıştırıyoruz:

 
  • [1-3]'te, URL ve [/getAllLongCategories] komutlarını HTTP ve GET komutları aracılığıyla çağırıyoruz;

Aşağıdaki yanıtı alıyoruz:

  • [1]'te, istemciden gelen HTTP isteği;
  • [2]'e, sunucunun HTTP yanıtı;
  • [3]'e, [200 OK] durumu, sunucunun isteği başarıyla işlediğini gösterir;
  • [4]'te, sunucunun jSON yanıtı;

jSON yanıtının tamamı şöyledir:


{"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, sunucu tarafında herhangi bir hata olmadığını gösterir;
  • exception: null, hata mesajı olmadığı anlamına gelir;
  • gövde: yanıtın gövdesidir; burada, ürünleri ile birlikte kategori listesi yer almaktadır. Her biri 5 ürün içeren iki kategori bulunmaktadır;

[categorie1] kategorisine [produit15] ürününü ekleyeceğiz. Bunun için, kalıcı hale getirilecek ürünlerin (ekleme/değişiklik) jSON dizisini bekleyen URL ve [/saveProduits] kodlarını kullanacağız. Bu dizi şu şekilde olacaktır:

[{"id":null,"version":null,"nom":"produit15","idCategorie":1881,"prix":111.0,"description":"desc15"}]}]

Web hizmetine / jSON adresine yapılan istek şu şekilde gerçekleştirilir:

  • [1]'te, istenen URL;
  • [2]'te, POST işlemi aracılığıyla talep edilir;
  • [3]'te, jSON dizesi gönderilir;
  • [4]'te, sunucuya jSON'i göndereceğimiz belirtilir;

Sunucunun yanıtı şöyledir:

  • [1]'te, [CoreProduit]'lerin bir listesini birincil anahtarlarıyla birlikte aldık. Burada, veritabanına az önce eklediğimiz ürünün birincil anahtarını içeren tek öğeli bir liste elde ettik;

Şimdi, [categorie[1]] adlı kategorinin uzun sürümünü isteyelim:

  • [1]'te, istenen URL;
  • [2]'e, POST'i oluşturuyoruz;
  • [3,4]'te, gönderilen değer bir jSON dizesidir. Bu, uzun versiyonunu istediğimiz kategori adlarının listesini temsil eder;

Sonuç olarak şunu elde ederiz:

  • [5]'te, [produit[1,5]] kategorisi artık altıncı bir ürüne sahiptir;

Şimdi bu ürünü silelim:

  • [1]'te, istenen URL;
  • [2]'e, POST'e dönüştürüyoruz;
  • [3-4]'e, silmek istediğimiz ürünlerin birincil anahtarlarının listesini temsil eden jSON dizesini göndeririz;

Elde edilen sonuç şöyledir:

 
  • [status:0], silme işleminin başarıyla gerçekleştirildiğini gösterir;

Şimdi, [body:[0]] ürününü sorgulayarak, ürünün gerçekten silindiğini doğrulayalım:

 

Şu sonucu alıyoruz:

 
  • [status:0], işlemin istisnasız bir şekilde gerçekleştirildiğini gösterir;
  • [produit[1,5]], [body]'in 0 öğeden oluşan bir liste olduğunu gösterir. Dolayısıyla [[${#httpServletRequest.remoteUser}]] öğesi başarıyla silinmiştir;

Tüm [GET] işlemleri basit bir tarayıcıda gerçekleştirilebilir:

Okuyucunun, web hizmetinin / jSON diğer URL işlemlerini denemesi önerilir.