A Guide to JavaLite - Een RESTful CRUD-applicatie bouwen

1. Inleiding

JavaLite is een verzameling frameworks om veelvoorkomende taken te vereenvoudigen waar elke ontwikkelaar mee te maken heeft bij het bouwen van applicaties.

In deze zelfstudie gaan we kijken naar JavaLite-functies die zijn gericht op het bouwen van een eenvoudige API.

2. Installatie

In deze tutorial zullen we een eenvoudige RESTful CRUD-applicatie maken. Om dat te doen, we gebruiken ActiveWeb en ActiveJDBC - twee van de frameworks waarmee JavaLite integreert.

Laten we dus aan de slag gaan en de eerste afhankelijkheid toevoegen die we nodig hebben:

 org.javalite activeweb 1.15 

ActiveWeb-artefact bevat ActiveJDBC, dus het is niet nodig om het afzonderlijk toe te voegen. Houd er rekening mee dat de nieuwste activeweb-versie te vinden is in Maven Central.

De tweede afhankelijkheid die we nodig hebben, is een databaseconnector. Voor dit voorbeeld gaan we MySQL gebruiken, dus we moeten het volgende toevoegen:

 mysql mysql-connector-java 5.1.45 

Nogmaals, de nieuwste mysql-connector-java-afhankelijkheid is te vinden op Maven Central.

De laatste afhankelijkheid die we moeten toevoegen, is iets specifieks voor JavaLite:

 org.javalite activejdbc-instrumentation 1.4.13 procesklassen instrument 

De nieuwste activejdbc-instrumentation-plug-in is ook te vinden in Maven Central.

Met dit alles op zijn plaats en voordat we beginnen met entiteiten, tabellen en toewijzingen, zullen we ervoor zorgen dat een van de ondersteunde databases is actief. Zoals we al eerder zeiden, gebruiken we MySQL.

Nu zijn we klaar om te beginnen met object-relationele mapping.

3. Object-relationele mapping

3.1. In kaart brengen en instrumentatie

Laten we beginnen met het creëren van een Product klasse die onze belangrijkste entiteit zal zijn:

product van openbare klasse {}

En laten we ook maak er de bijbehorende tabel voor:

CREATE TABLE PRODUCTS (id int (11) STANDAARD NULL auto_increment PRIMAIRE SLEUTEL, naam VARCHAR (128));

Eindelijk kunnen we wijzigen onze Product klasse om de mapping te doen:

public class Product breidt model {} uit

We hoeven alleen maar uit te breiden org.javalite.activejdbc.Model klasse. ActiveJDBC leidt DB-schemaparameters af uit de database. Dankzij deze mogelijkheid, het is niet nodig om getters en setters of annotaties toe te voegen.

Bovendien herkent ActiveJDBC dat automatisch Product klasse moet worden toegewezen aan PRODUCTEN tafel. Het maakt gebruik van Engelse verbuigingen om de enkelvoudsvorm van een model om te zetten in een meervoudsvorm van een tabel. En ja, het werkt ook met uitzonderingen.

Er is nog een laatste ding dat we nodig zullen hebben om ons in kaart brengen te laten werken: instrumentatie. Instrumentatie is een extra stap vereist door ActiveJDBC dat zal ons toelaten om te spelen met onze Product class alsof het getters, setters en DAO-achtige methoden heeft.

Nadat we instrumentatie hebben uitgevoerd, kunnen we dingen doen als:

Product p = nieuw product (); p.set ("naam", "Brood"); p.saveIt ();

of:

Lijst producten = Product.findAll ();

Dit is waar activejdbc-instrumentatie plug-in komt binnen. Omdat we de afhankelijkheid al hebben in onze pom, zouden we klassen moeten zien die tijdens het bouwen worden geïnstrumenteerd:

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

Vervolgens maken we een eenvoudige test om te controleren of dit werkt.

3.2. Testen

Ten slotte volgen we drie eenvoudige stappen om onze mapping te testen: een verbinding met de database openen, een nieuw product opslaan en het ophalen:

@Test openbare ongeldig gegevenSavedProduct_WhenFindFirst_ThenSavedProductIsReturned () {Base.open ("com.mysql.jdbc.Driver", "jdbc: mysql: // localhost / dbname", "gebruiker", "wachtwoord"); Product toSaveProduct = nieuw product (); toSaveProduct.set ("naam", "Brood"); toSaveProduct.saveIt (); Product opgeslagenProduct = Product.findFirst ("naam =?", "Brood"); assertEquals (toSaveProduct.get ("naam"), opgeslagenProduct.get ("naam")); }

Merk op dat dit alles (en meer) mogelijk is door alleen een leeg model en instrumentatie te hebben.

4. Controllers

Nu onze mapping klaar is, kunnen we gaan nadenken over onze applicatie en zijn CRUD-methoden.

Daarvoor gaan we gebruik maken van controllers die HTTP-verzoeken verwerken.

Laten we onze ProductenController:

@RESTful public class ProductsController breidt AppController uit {public void index () {// ...}}

Met deze implementatie zal ActiveWeb automatisch in kaart brengen inhoudsopgave() methode naar de volgende URI:

//: / producten

Controllers geannoteerd met @RESTful, bieden een vaste set methoden die automatisch zijn toegewezen aan verschillende URI's. Laten we eens kijken welke nuttig zullen zijn voor ons CRUD-voorbeeld:

Controller-methodeHTTP-methodeURI
CREËERmaken ()POST// host: poort / producten
LEES EENtonen()KRIJGEN// host: poort / producten / {id}
ALLES LEZENinhoudsopgave()KRIJGEN// host: poort / producten
BIJWERKENbijwerken()LEGGEN// host: poort / producten / {id}
VERWIJDERENvernietigen()VERWIJDEREN// host: poort / producten / {id}

En als we deze reeks methoden toevoegen aan onze ProductenController:

@RESTful public class ProductsController breidt AppController {public void index () {// code uit om alle producten te krijgen} public void create () {// code om een ​​nieuw product te maken} public void update () {// code om een ​​bestaand product bij te werken product} public void show () {// code om één product te vinden} public void destroy () {// code om een ​​bestaand product te verwijderen}}

Voordat we verder gaan met onze logische implementatie, zullen we snel een paar dingen bekijken die we moeten configureren.

5. Configuratie

ActiveWeb is voornamelijk gebaseerd op conventies, de projectstructuur is daar een voorbeeld van. ActiveWeb-projecten moeten een vooraf gedefinieerde pakketlay-out volgen:

src | ---- main | ---- java.app | | ---- config | | ---- regelaars | | ---- modellen | ---- bronnen | ---- webapp | ---- WEB-INF | ---- weergaven

Er is een specifiek pakket waar we naar moeten kijken - eenpp.config.

Binnen dat pakket gaan we drie klassen maken:

openbare klasse DbConfig breidt AbstractDBConfig {@Override public void init (AppContext appContext) {this.configFile ("/ database.properties") uit; }}

Deze klasse configureert databaseverbindingen met behulp van een eigenschappenbestand in de hoofdmap van het project met de vereiste parameters:

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

Hierdoor wordt de verbinding automatisch gemaakt ter vervanging van wat we in de eerste regel van onze mappingtest hebben gedaan.

De tweede klasse die we binnen moeten opnemen app.config pakket is:

public class AppControllerConfig breidt AbstractControllerConfig {@Override public void init (AppContext appContext) {add (new DBConnectionFilter ()) uit naar (ProductsController.class); }}

Deze codebindt de verbinding die we zojuist hebben geconfigureerd aan onze controller.

De derde klas zullenconfigureer de context van onze app:

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

Na het maken van de drie klassen is het laatste wat de configuratie betreft het creëren van onze web.xml het dossier onder webapp / WEB-INF directory:

   dispatcher org.javalite.activeweb.RequestDispatcher uitsluitingen css, afbeeldingen, js, ico-codering UTF-8 dispatcher / * 

Nu de configuratie is voltooid, kunnen we doorgaan en onze logica toevoegen.

6. Implementatie van CRUD Logic

Met de DAO-achtige mogelijkheden van onze Product klasse, het is super eenvoudig om voeg basis CRUD-functionaliteit toe:

@RESTful public class ProductsController breidt AppController uit {private ObjectMapper mapper = new ObjectMapper (); public void index () {List products = Product.findAll (); // ...} public void create () {Map payload = mapper.readValue (getRequestString (), Map.class); Product p = nieuw 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 (); // ...}}

Makkelijk toch? Dit retourneert echter nog niets. Om dat te kunnen doen, moeten we een aantal views creëren.

7. Bekeken

ActiveWeb gebruikt FreeMarker als een sjabloon-engine, en al zijn sjablonen zouden onder src / main / webapp / WEB-INF / views.

Binnen die map plaatsen we onze weergaven in een map met de naam producten (hetzelfde als onze controller). Laten we onze eerste sjabloon maken met de naam _product.ftl:

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

Het is op dit punt vrij duidelijk dat dit een JSON-reactie is. Dit werkt natuurlijk maar voor één product, dus laten we doorgaan en een ander sjabloon maken met de naam index.ftl:

[]

Dit geeft in feite een verzameling met de naam producten, met elk geformatteerd door _product.ftl.

Tenslotte, we moeten het resultaat van onze controller binden aan de overeenkomstige weergave:

@RESTful public class ProductsController breidt AppController uit {public void index () {List products = Product.findAll (); view ("producten", producten); render (); } public void show () {String id = getId (); Product p = Product.findById (id); view ("product", p); render ("_ product"); }}

In het eerste geval wijzen we toe producten lijst naar onze sjabloonverzameling met de naam ook producten.

Aangezien we geen weergave specificeren, index.ftl zal gebruikt worden.

Bij de tweede methode wijzen we een product toe p naar element Product in de weergave en we zeggen expliciet welke weergave moet worden weergegeven.

We zouden ook een uitzicht kunnen creëren message.ftl:

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

En noem het dan een van onze ProductenController‘S methode:

view ("bericht", "Er is een fout opgetreden.", "code", 200); render ("bericht");

Laten we nu naar onze finale kijken ProductenController:

@RESTful public class ProductsController breidt AppController uit {private ObjectMapper mapper = new ObjectMapper (); public void index () {view ("producten", Product.findAll ()); render (). contentType ("applicatie / json"); } public void create () {Map payload = mapper.readValue (getRequestString (), Map.class); Product p = nieuw product (); p.fromMap (payload); p.saveIt (); view ("bericht", "Succesvol opgeslagen product-id" + p.get ("id"), "code", 200); render ("bericht"); } public void update () {Map payload = mapper.readValue (getRequestString (), Map.class); String id = getId (); Product p = Product.findById (id); if (p == null) {view ("bericht", "Product-id" + id + "niet gevonden.", "code", 200); render ("bericht"); terugkeren; } p.fromMap (payload); p.saveIt (); view ("bericht", "Succesvol bijgewerkte product-id" + id, "code", 200); render ("bericht"); } public void show () {String id = getId (); Product p = Product.findById (id); if (p == null) {view ("bericht", "Product-id" + id + "niet gevonden.", "code", 200); render ("bericht"); terugkeren; } view ("product", p); render ("_ product"); } public void destroy () {String id = getId (); Product p = Product.findById (id); if (p == null) {view ("bericht", "Product-id" + id + "niet gevonden.", "code", 200); render ("bericht"); terugkeren; } p.delete (); view ("bericht", "Product-id succesvol verwijderd" + id, "code", 200); render ("bericht"); } @Override beschermde String getContentType () {return "application / json"; } @Override beschermde String getLayout () {return null; }}

Op dit punt is onze applicatie klaar en zijn we klaar om deze uit te voeren.

8. De toepassing uitvoeren

We gebruiken de Jetty-plug-in:

 org.eclipse.jetty jetty-maven-plugin 9.4.8.v20171121 

Vind de nieuwste steiger-maven-plug-in in Maven Central.

En we zijn klaar we kunnen onze applicatie draaien:

mvn steiger: run

Laten we een paar producten maken:

$ curl -X POST // localhost: 8080 / products -H 'content-type: application / json' -d '{"name": "Water"}' {"message": "Succesvol opgeslagen product-id 1", " code ": 200}
$ curl -X POST // localhost: 8080 / products -H 'content-type: application / json' -d '{"name": "Bread"}' {"message": "Succesvol opgeslagen product-id 2", " code ": 200}

.. Lees ze:

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

.. update een van hen:

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

... lees degene die we zojuist hebben bijgewerkt:

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

Ten slotte kunnen we er een verwijderen:

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

9. Conclusie

JavaLite heeft veel tools om ontwikkelaars te helpen binnen enkele minuten een applicatie aan de praat krijgen. Hoewel het baseren van dingen op conventies resulteert in een schonere en eenvoudigere code, duurt het even om de naamgeving en locatie van klassen, pakketten en bestanden te begrijpen.

Dit was slechts een inleiding tot ActiveWeb en ActiveJDBC, vind meer documentatie op hun website en zoek naar onze producttoepassing in het Github-project.