Ein Leitfaden für JavaLite - Erstellen einer RESTful CRUD-Anwendung

1. Einführung

  • JavaLite ist eine Sammlung von Frameworks zur Vereinfachung allgemeiner Aufgaben ** , die jeder Entwickler beim Erstellen von Anwendungen erledigen muss.

In diesem Tutorial werfen wir einen Blick auf die JavaLite-Funktionen, die sich auf die Erstellung einer einfachen API konzentrieren.

2. Konfiguration

In diesem Lernprogramm erstellen wir eine einfache RESTful CRUD-Anwendung. Zu diesem Zweck verwenden wir ActiveWeb und ActiveJDBC - zwei der Frameworks, in die JavaLite integriert ist.

Also fangen wir an und fügen die erste Abhängigkeit hinzu, die wir brauchen:

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

ActiveWeb-Artefakte enthalten ActiveJDBC, sodass es nicht erforderlich ist, es separat hinzuzufügen. Bitte beachten Sie, dass die neueste Version von activeweb gefunden werden kann in Maven Central.

Die zweite Abhängigkeit, die wir brauchen, ist ein Datenbankkonnektor . In diesem Beispiel werden wir MySQL verwenden, also müssen wir Folgendes hinzufügen:

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

Die letzte Abhängigkeit, die wir hinzufügen müssen, ist etwas spezifisch für 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>

Das neueste activejdbc-instrumentation plugin kann auch in Maven Central gefunden werden.

Wenn all dies vorhanden ist und bevor wir mit Entitäten, Tabellen und Zuordnungen beginnen, stellen wir sicher, dass eine der supported database läuft . Wie bereits erwähnt, verwenden wir MySQL.

Nun können wir mit der objektrelationalen Zuordnung beginnen.

3. Objektbezogene Zuordnung

3.1. Mapping und Instrumentierung

Beginnen wir mit dem Erstellen einer Product -Klasse, die unsere Hauptentität sein wird:

public class Product {}

Und lass uns auch die entsprechende Tabelle dazu erstellen :

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

Schließlich können wir unsere Product -Klasse ändern, um das Mapping durchzuführen :

public class Product extends Model {}

Wir müssen nur die Klasse org.javalite.activejdbc.Model erweitern.

  • ActiveJDBC entnimmt DB-Schemaparameter aus der Datenbank ** . Dank dieser Funktion müssen keine Getter und Setter oder Anmerkungen hinzugefügt werden.

Darüber hinaus erkennt ActiveJDBC automatisch, dass die Product -Klasse der PRODUCTS -Tabelle zugeordnet werden muss. Es verwendet englische Beugungen, um die Singularform eines Modells in eine Pluralform einer Tabelle umzuwandeln. Und ja, es funktioniert auch mit Ausnahmen.

Es gibt noch eine letzte Sache, die wir benötigen, um unser Mapping zu funktionieren:

Instrumentierung. Instrumentation ist ein zusätzlicher Schritt, der von ActiveJDBC benötigt wird , der es uns ermöglicht, mit unserer Product -Klasse so zu spielen, als hätte sie Getter, Setter und DAO-ähnliche Methoden.

Nachdem wir die Instrumentation ausgeführt haben, können wir Folgendes tun:

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

oder:

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

Hier kommt das activejdbc-instrumentation plugin ins Spiel. Da wir bereits eine Abhängigkeit in unserem pom haben, sollten Klassen beim Bau instrumentiert werden:

...[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 ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
...

Als Nächstes erstellen wir einen einfachen Test, um sicherzustellen, dass dies funktioniert.

3.2. Testen

Um unser Mapping zu testen, folgen wir drei einfachen Schritten: Öffnen Sie eine Verbindung zur Datenbank, speichern Sie ein neues Produkt und rufen Sie es ab:

@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"));
}

Beachten Sie, dass dies (und mehr) nur durch ein leeres Modell und eine leere Instrumentation möglich ist.

4. Steuerungen

Nun, da unser Mapping fertig ist, können wir über unsere Anwendung und ihre CRUD-Methoden nachdenken.

Dafür werden wir Controller verwenden, die HTTP-Anfragen verarbeiten.

Lassen Sie uns unseren ProductsController erstellen:

@RESTful
public class ProductsController extends AppController {

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

}

Mit dieser Implementierung ordnet ActiveWeb die Methode index () automatisch dem folgenden URI zu:

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

Controller, die mit @ RESTful , kommentiert wurden, stellen einen festen Satz von Methoden bereit, die automatisch verschiedenen URIs zugeordnet werden. ** Schauen wir uns die Methoden an, die für unser CRUD-Beispiel nützlich sind:

| ================================================== ============== | Controller-Methode | HTTP-Methode | URI | | CREATE | create () | POST | http://host : port/products | LESEN SIE EIN | show () | GET | http://host : port/products/\ {id} | ALLES LESEN | index () | GET | http://host : port/products | UPDATE | update () | PUT | http://host : port/products/\ {id} | DELETE | destroy () | DELETE | http://host : port/products/\ {id} | ================================== =============================

Und wenn wir diesen Satz von Methoden zu unserem ProductsController hinzufügen:

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

Bevor wir zur Logikimplementierung übergehen, werfen wir einen kurzen Blick auf einige Dinge, die wir konfigurieren müssen.

5. Aufbau

ActiveWeb basiert hauptsächlich auf Konventionen, die Projektstruktur ist ein Beispiel dafür. ActiveWeb-Projekte müssen einem vordefinierten Paketlayout folgen :

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

Es gibt ein bestimmtes Paket, das wir uns ansehen müssen - a _pp.config _ .

In diesem Paket erstellen wir drei Klassen:

public class DbConfig extends AbstractDBConfig {
    @Override
    public void init(AppContext appContext) {
        this.configFile("/database.properties");
    }
}
  • Diese Klasse konfiguriert Datenbankverbindungen ** mithilfe einer Eigenschaftendatei im Stammverzeichnis des Projekts, die die erforderlichen Parameter enthält:

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

Dadurch wird die Verbindung automatisch erstellt und das ersetzt, was wir in der ersten Zeile unseres Mapping-Tests durchgeführt haben.

Die zweite Klasse, die wir in das app.config -Paket aufnehmen müssen, lautet:

public class AppControllerConfig extends AbstractControllerConfig {

    @Override
    public void init(AppContext appContext) {
        add(new DBConnectionFilter()).to(ProductsController.class);
    }
}
  • Dieser Code bindet die gerade konfigurierte Verbindung an unseren Controller. **

Die dritte Klasse konfiguriert den Kontext unserer App ** :

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

Nach dem Erstellen der drei Klassen ist das letzte, was die Konfiguration betrifft, das Erstellen unserer web.xml -Datei unter dem Verzeichnis 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>

Nachdem diese Konfiguration abgeschlossen ist, können wir unsere Logik hinzufügen.

6. CRUD-Logik implementieren

Mit den DAO-ähnlichen Funktionen unserer Product -Klasse ist es äußerst einfach, einfache CRUD-Funktionen hinzuzufügen

@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();
       //...
    }
}

Einfach richtig? Dies gibt jedoch noch nichts zurück. Dazu müssen wir einige Ansichten erstellen.

7. Ansichten

  • ActiveWeb wird als Templating Engine verwendet, und alle seine Vorlagen sollten sich unter src/main/webapp/WEB-INF/views befinden. **

In diesem Verzeichnis werden unsere Ansichten in einem Ordner mit dem Namen products abgelegt (derselbe wie unser Controller). Erstellen wir unsere erste Vorlage mit dem Namen __product.ftl :

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

An diesem Punkt ist es ziemlich klar, dass dies eine JSON-Antwort ist. Natürlich funktioniert das nur für ein Produkt, also erstellen wir eine andere Vorlage namens index.ftl :

----[<@render partial="product" collection=products/>]----
  • Dadurch wird grundsätzlich eine Sammlung mit dem Namen products gerendert, wobei jede mit __product.ftl formatiert ist. **

Schließlich müssen wir das Ergebnis von unserem Controller an die entsprechende Ansicht binden

@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");
    }
}

Im ersten Fall weisen wir unserer Vorlagensammlung products liste products__ zu.

Da wir keine Ansicht angeben, wird index.ftl verwendet.

Bei der zweiten Methode weisen wir dem Element product in der Ansicht das Produkt p zu und sagen ausdrücklich, welche Ansicht gerendert werden soll.

Wir könnten auch eine Sicht message.ftl erstellen:

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

Und dann rufen Sie es aus einer unserer ProductsController s-Methode auf:

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

Nun sehen wir unseren endgültigen ProductsController :

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

An diesem Punkt ist unsere Anwendung fertig und wir können sie ausführen.

8. Anwendung ausführen

Wir verwenden das Jetty-Plugin:

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

Und wir sind bereit, wir können Ihre Bewerbung ausführen :

mvn jetty:run

Lassen Sie uns ein paar Produkte erstellen:

$ 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. Lese sie:

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

.. eine von ihnen aktualisieren:

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

... lesen Sie den, den wir gerade aktualisiert haben:

[source,bash,gutter:,true]

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

Zum Schluss können wir einen löschen:

[source,bash,gutter:,true]

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

[[conclusions]]

===  **  9. Fazit**

JavaLite bietet eine Vielzahl von Tools, die Entwicklern helfen, eine Anwendung innerhalb von Minuten in Betrieb zu nehmen. Während die Konventionen zwar zu einem saubereren und einfacheren Code führen, dauert es jedoch eine Weile, bis die Benennung und der Speicherort von Klassen, Paketen und Dateien bekannt sind.

Dies war nur eine Einführung in ActiveWeb und ActiveJDBC. Weitere Informationen finden Sie auf ihrer http://javalite.io[Website]. Suchen Sie nach unserer Produktanwendung unter https://github.com/eugenp/tutorials/tree/master/java- lite[das Github-Projekt].