Guide de JavaLite - Construction d’une application RESTful CRUD

1. Introduction

  • JavaLite est un ensemble de cadres de simplification des tâches courantes ** que chaque développeur doit traiter lors de la création d’applications.

Dans ce tutoriel, nous allons examiner les fonctionnalités de JavaLite axées sur la création d’une API simple.

2. Installer

Tout au long de ce didacticiel, nous allons créer une application RESTful CRUD simple. Pour ce faire, nous utiliserons ActiveWeb et ActiveJDBC , deux des infrastructures avec lesquelles JavaLite s’intègre.

Commençons donc par ajouter la première dépendance dont nous avons besoin:

<dependency>
    <groupId>org.javalite</groupId>
    <artifactId>activeweb</artifactId>
    <version>1.15</version>
</dependency>

L’artéfact ActiveWeb inclut ActiveJDBC, il n’est donc pas nécessaire de l’ajouter séparément. Veuillez noter que la dernière version https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.javalite%22%20AND%20a%3A%22activeweb%22&activeweb ]est disponible. dans Maven Central.

La deuxième dépendance dont nous avons besoin est un connecteur de base de données . Pour cet exemple, nous allons utiliser MySQL, nous devons donc ajouter:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.45</version>
</dependency>

Encore une fois, le dernier mysql-connector-java la dépendance peut être trouvée sur Maven Central.

La dernière dépendance à ajouter est quelque chose de spécifique à JavaLite:

<plugin>
    <groupId>org.javalite</groupId>
    <artifactId>activejdbc-instrumentation</artifactId>
    <version>1.4.13</version>
    <executions>
        <execution>
            <phase>process-classes</phase>
            <goals>
                <goal>instrument</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Ayant tout cela en place et avant de commencer par les entités, les tables et les mappages, nous allons nous assurer que l’un des supported bases de données est opérationnel . Comme nous l’avons déjà dit, nous utiliserons MySQL.

Nous sommes maintenant prêts à commencer avec le mappage objet-relationnel.

3. Cartographie objet-relationnel

3.1. Cartographie et Instrumentation

Commençons par créer une classe Product qui sera notre entité principale :

public class Product {}

Et créons également la table correspondante :

CREATE TABLE PRODUCTS (
    id int(11) DEFAULT NULL auto__increment PRIMARY KEY,
    name VARCHAR(128)
);

Enfin, nous pouvons modifier notre classe Product pour faire le mapping :

public class Product extends Model {}

Nous avons seulement besoin d’étendre la classe org.javalite.activejdbc.Model .

  • ActiveJDBC déduit les paramètres de schéma de la base de données . Grâce à cette fonctionnalité , il n’est pas nécessaire d’ajouter des accesseurs, des calculateurs ou des annotations ** .

En outre, ActiveJDBC reconnaît automatiquement que la classe Product doit être mappée à la table PRODUCTS . Il utilise les inflexions anglaises pour convertir la forme singulière d’un modèle en une forme plurielle de table. Et oui, cela fonctionne également avec des exceptions.

Il nous reste une dernière chose à faire pour que notre cartographie fonctionne:

instrumentation. Instrumentation est une étape supplémentaire requise par ActiveJDBC qui nous permettra de jouer avec notre classe Product comme si elle disposait de getters, de setters et de méthodes de type DAO.

Après avoir utilisé l’instrumentation, nous pourrons faire des choses comme:

Product p = new Product();
p.set("name","Bread");
p.saveIt();

ou:

List<Product> products = Product.findAll();

C’est ici qu’intervient le plugin activejdbc-instrumentation . Comme nous avons déjà la dépendance dans notre pom, nous devrions voir les classes être instrumentées pendant la construction:

...[INFO]--- activejdbc-instrumentation:1.4.11:instrument (default) @ javalite ---
** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **  START INSTRUMENTATION ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
Directory: ...\tutorials\java-lite\target\classes
Instrumented class: .../tutorials/java-lite/target/classes/app/models/Product.class
** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **  END INSTRUMENTATION ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
...

Ensuite, nous allons créer un test simple pour nous assurer que cela fonctionne.

3.2. Essai

Enfin, pour tester notre correspondance, nous allons suivre trois étapes simples: ouvrir une connexion à la base de données, enregistrer un nouveau produit et le récupérer:

@Test
public void givenSavedProduct__WhenFindFirst__ThenSavedProductIsReturned() {

    Base.open(
      "com.mysql.jdbc.Driver",
      "jdbc:mysql://localhost/dbname",
      "user",
      "password");

    Product toSaveProduct = new Product();
    toSaveProduct.set("name", "Bread");
    toSaveProduct.saveIt();

    Product savedProduct = Product.findFirst("name = ?", "Bread");

    assertEquals(
      toSaveProduct.get("name"),
      savedProduct.get("name"));
}

Notez que tout cela (et plus) est possible en n’ayant qu’un modèle et une instrumentation vides.

4. Contrôleurs

Maintenant que notre cartographie est prête, nous pouvons commencer à réfléchir à notre application et à ses méthodes CRUD.

Pour cela, nous allons utiliser des contrôleurs qui traitent les requêtes HTTP.

Créons notre ProductsController :

@RESTful
public class ProductsController extends AppController {

    public void index() {
       //...
    }

}

Avec cette implémentation, ActiveWeb mappera automatiquement la méthode index () à l’URI suivant:

http://<host>:<port>/products

Les contrôleurs annotés avec @ RESTful , fournissent un ensemble fixe de méthodes automatiquement mappées sur différents URI. Voyons celles qui seront utiles pour notre exemple CRUD:

| ============================================== ============= | Méthode du contrôleur | Méthode HTTP | URI | | CREATE | create () | POST | http://hôte : port/produits | LIRE UN | show () | GET | http://hôte : port/produits/\ {id} | LIRE TOUT | index () | GET | http://hôte : port/produits | UPDATE | update () | PUT | http://hôte : port/produits/\ {id} | DELETE | destroy () | DELETE | http://hôte : port/produits/\ {id} | ================================= ============================

Et si nous ajoutons cet ensemble de méthodes à notre ProductsController :

@RESTful
public class ProductsController extends AppController {

    public void index() {
       //code to get all products
    }

    public void create() {
       //code to create a new product
    }

    public void update() {
       //code to update an existing product
    }

    public void show() {
       //code to find one product
    }

    public void destroy() {
       //code to remove an existing product
    }
}

Avant de passer à notre implémentation logique, nous examinerons rapidement quelques éléments à configurer.

5. Configuration

ActiveWeb est basé principalement sur des conventions, la structure de projet en est un exemple. Les projets ActiveWeb doivent suivre une présentation de package prédéfinie :

src
 |----main
       |----java.app
       |     |----config
       |     |----controllers
       |     |----models
       |----resources
       |----webapp
             |----WEB-INF
             |----views

Il y a un paquet spécifique sur lequel nous devons jeter un œil - un _pp.config _ .

Dans ce package, nous allons créer trois classes:

public class DbConfig extends AbstractDBConfig {
    @Override
    public void init(AppContext appContext) {
        this.configFile("/database.properties");
    }
}
  • Cette classe configure les connexions à la base de données ** en utilisant un fichier de propriétés dans le répertoire racine du projet contenant les paramètres requis:

development.driver=com.mysql.jdbc.Driver
development.username=user
development.password=password
development.url=jdbc:mysql://localhost/dbname

Cela créera la connexion en remplaçant automatiquement ce que nous avons fait dans la première ligne de notre test de mappage.

La deuxième classe que nous devons inclure à l’intérieur du paquet app.config est:

public class AppControllerConfig extends AbstractControllerConfig {

    @Override
    public void init(AppContext appContext) {
        add(new DBConnectionFilter()).to(ProductsController.class);
    }
}
  • Ce code liera la connexion que nous venons de configurer à notre contrôleur. **

La troisième classe va configurer le contexte de notre application :

public class AppBootstrap extends Bootstrap {
    public void init(AppContext context) {}
}

Après avoir créé les trois classes, la dernière chose concernant la configuration est la création de notre fichier web.xml sous le répertoire webapp/WEB-INF :

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns=...>

    <filter>
        <filter-name>dispatcher</filter-name>
        <filter-class>org.javalite.activeweb.RequestDispatcher</filter-class>
        <init-param>
            <param-name>exclusions</param-name>
            <param-value>css,images,js,ico</param-value>
        </init-param>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>dispatcher</filter-name>
        <url-pattern>/** </url-pattern>
    </filter-mapping>

</web-app>

Maintenant que la configuration est terminée, nous pouvons continuer et ajouter notre logique.

6. Implémentation de la logique CRUD

Avec les fonctionnalités de type DAO fournies par notre classe Product , il est extrêmement simple d’ajouter des fonctionnalités de base CRUD :

@RESTful
public class ProductsController extends AppController {

    private ObjectMapper mapper = new ObjectMapper();

    public void index() {
        List<Product> products = Product.findAll();
       //...
    }

    public void create() {
        Map payload = mapper.readValue(getRequestString(), Map.class);
        Product p = new Product();
        p.fromMap(payload);
        p.saveIt();
       //...
    }

    public void update() {
        Map payload = mapper.readValue(getRequestString(), Map.class);
        String id = getId();
        Product p = Product.findById(id);
        p.fromMap(payload);
        p.saveIt();
       //...
    }

    public void show() {
        String id = getId();
        Product p = Product.findById(id);
       //...
    }

    public void destroy() {
        String id = getId();
        Product p = Product.findById(id);
        p.delete();
       //...
    }
}

Facile, non? Cependant, cela ne retourne rien pour l’instant. Pour ce faire, nous devons créer des vues.

7. Vues

  • ActiveWeb est utilisé comme moteur de templates et tous ses modèles doivent être situés sous src/main/webapp/WEB-INF/views . **

Dans ce répertoire, nous placerons nos vues dans un dossier appelé products (identique à notre contrôleur). Créons notre premier modèle appelé __product.ftl :

{
    "id" : ${product.id},
    "name" : "${product.name}"
}

C’est assez clair à ce stade qu’il s’agit d’une réponse JSON. Bien sûr, cela ne fonctionnera que pour un produit, alors allons-y et créons un autre modèle appelé index.ftl :

----[<@render partial="product" collection=products/>]----
  • Ceci rendra essentiellement une collection nommée products , avec chacune formatée par __product.ftl . **

Enfin, nous devons lier le résultat de notre contrôleur à la vue correspondante :

@RESTful
public class ProductsController extends AppController {

    public void index() {
        List<Product> products = Product.findAll();
        view("products", products);
        render();
    }

    public void show() {
        String id = getId();
        Product p = Product.findById(id);
        view("product", p);
        render("__product");
    }
}

Dans le premier cas, nous assignons products list à notre collection de modèles nommée également products .

Ensuite, comme nous ne spécifions aucune vue, index.ftl sera utilisé.

Dans la deuxième méthode, nous affectons le produit p à l’élément product de la vue et nous indiquons explicitement la vue à restituer.

Nous pourrions aussi créer une vue message.ftl :

{
    "message" : "${message}",
    "code" : ${code}
}

Puis appelez-la à l’une des méthodes de notre ProductsController :

view("message", "There was an error.", "code", 200);
render("message");

Voyons maintenant notre ProductsController final:

@RESTful
public class ProductsController extends AppController {

    private ObjectMapper mapper = new ObjectMapper();

    public void index() {
        view("products", Product.findAll());
        render().contentType("application/json");
    }

    public void create() {
        Map payload = mapper.readValue(getRequestString(), Map.class);
        Product p = new Product();
        p.fromMap(payload);
        p.saveIt();
        view("message", "Successfully saved product id " + p.get("id"), "code", 200);
        render("message");
    }

    public void update() {
        Map payload = mapper.readValue(getRequestString(), Map.class);
        String id = getId();
        Product p = Product.findById(id);
        if (p == null) {
            view("message", "Product id " + id + " not found.", "code", 200);
            render("message");
            return;
        }
        p.fromMap(payload);
        p.saveIt();
        view("message", "Successfully updated product id " + id, "code", 200);
        render("message");
    }

    public void show() {
        String id = getId();
        Product p = Product.findById(id);
        if (p == null) {
            view("message", "Product id " + id + " not found.", "code", 200);
            render("message");
            return;
        }
        view("product", p);
        render("__product");
    }

    public void destroy() {
        String id = getId();
        Product p = Product.findById(id);
        if (p == null) {
            view("message", "Product id " + id + " not found.", "code", 200);
            render("message");
            return;
        }
        p.delete();
        view("message", "Successfully deleted product id " + id, "code", 200);
        render("message");
    }

    @Override
    protected String getContentType() {
        return "application/json";
    }

    @Override
    protected String getLayout() {
        return null;
    }
}

À ce stade, notre application est terminée et nous sommes prêts à l’exécuter.

8. Lancer l’application

Nous allons utiliser le plugin Jetty:

<plugin>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-maven-plugin</artifactId>
    <version>9.4.8.v20171121</version>
</plugin>

Et nous sommes prêts , nous pouvons exécuter notre application :

mvn jetty:run

Créons quelques produits:

$ curl -X POST http://localhost:8080/products
  -H 'content-type: application/json'
  -d '{"name":"Water"}'
{
    "message" : "Successfully saved product id 1",
    "code" : 200
}
$ curl -X POST http://localhost:8080/products
  -H 'content-type: application/json'
  -d '{"name":"Bread"}'
{
    "message" : "Successfully saved product id 2",
    "code" : 200
}
  1. Lis-les:

$ curl -X GET http://localhost:8080/products[    {
        "id" : 1,
        "name" : "Water"
    },
    {
        "id" : 2,
        "name" : "Bread"
    }]----

.. mettre à jour l'un d'entre eux:

[source,bash,gutter:,true]

$ curl -X PUT http://localhost:8080/products/1 -H 'content-type: application/json' -d '{"name":"Juice"}' { "message" : "Successfully updated product id 1", "code" : 200 }

... lisez celui que nous venons de mettre à jour:

[source,bash,gutter:,true]

$ curl -X GET http://localhost:8080/products/1 { "id" : 1, "name" : "Juice" }

Enfin, nous pouvons en supprimer un:

[source,bash,gutter:,true]

$ curl -X DELETE http://localhost:8080/products/2 { "message" : "Successfully deleted product id 2", "code" : 200 }

[[conclusions]]

===  **  9. Conclusion**

JavaLite propose de nombreux outils pour aider les développeurs à **  mettre en place une application en quelques minutes ** . Cependant, si le fait de se baser sur des conventions donne un code plus propre et plus simple, il faut un certain temps pour comprendre le nom et l'emplacement des classes, des packages et des fichiers.

Ce n'était qu'une introduction à ActiveWeb et ActiveJDBC, retrouvez plus de documentation sur leur http://javalite.io[site web]et recherchez notre application de produits dans https://github.com/eugenp/tutorials/tree/master/java- Lite[le projet Github].