JavaLiteのガイド– RESTfulCRUDアプリケーションの構築

1.はじめに

JavaLiteは、アプリケーションを構築するときにすべての開発者が処理しなければならない一般的なタスクを簡素化するためのフレームワークのコレクションです。

このチュートリアルでは、単純なAPIの構築に焦点を当てたJavaLiteの機能を見ていきます。

2.セットアップ

このチュートリアル全体を通して、単純なRESTfulCRUDアプリケーションを作成します。そのために、JavaLiteが統合されている2つのフレームワークであるActiveWebとActiveJDBCを使用します。

それでは、始めて、必要な最初の依存関係を追加しましょう。

 org.javalite activeweb 1.15 

ActiveWebアーティファクトにはActiveJDBCが含まれているため、個別に追加する必要はありません。最新のactivewebバージョンはMavenCentralにあることに注意してください。

必要な2番目の依存関係は、データベースコネクタです。この例では、MySQLを使用するため、以下を追加する必要があります。

 mysql mysql-connector-java 5.1.45 

繰り返しになりますが、最新のmysql-connector-java依存関係は、MavenCentralにあります。

追加する必要がある最後の依存関係は、JavaLiteに固有のものです。

 org.javalite activejdbc-instrumentation 1.4.13   process-classes  instrument    

最新のactivejdbc-instrumentationプラグインは、MavenCentralにもあります。

これらすべてを適切に配置し、エンティティ、テーブル、およびマッピングを開始する前に、サポートされているデータベースの1つが稼働していることを確認します。前に述べたように、MySQLを使用します。

これで、オブジェクトリレーショナルマッピングを開始する準備が整いました。

3.オブジェクトリレーショナルマッピング

3.1。マッピングとインストルメンテーション

メインエンティティとなるProductクラスを作成することから始めましょう:

public class Product {}

そして、それに対応するテーブル作成しましょう:

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

最後に、Productクラスを変更してマッピングを行うことができます

public class Product extends Model {}

org.javalite.activejdbc.Modelクラスを拡張するだけで済みます。ActiveJDBCは、データベースからDBスキーマパラメータを推測します。この機能のおかげで、ゲッターやセッター、または注釈を追加する必要ありません

さらに、ActiveJDBCは、ProductクラスをPRODUCTSテーブルにマップする必要があることを自動的に認識します。これは、英語の語尾変化を利用して、モデルの単数形をテーブルの複数形に変換します。そして、はい、それは例外でも機能します。

マッピングを機能させるために必要な最後の1つが、インストルメンテーションです。インストルメンテーションは、ActiveJDBCに必要な追加の手順であり、ゲッター、セッター、およびDAOのようなメソッドがあるかのようにProductクラスで遊ぶことができます。

インストルメンテーションを実行すると、次のようなことができるようになります。

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

または:

List products = Product.findAll();

ここでactivejdbc-instrumentationプラグインが登場します。pomにはすでに依存関係があるため、ビルド中にクラスがインストルメントされていることがわかります。

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

次に、これが機能していることを確認するための簡単なテストを作成します。

3.2。テスト

最後に、マッピングをテストするために、3つの簡単な手順に従います。データベースへの接続を開き、新しい製品を保存して取得します。

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

空のモデルとインストルメンテーションを用意するだけで、これらすべて(およびそれ以上)が可能になることに注意してください。

4.コントローラー

マッピングの準備ができたので、アプリケーションとそのCRUDメソッドについて考え始めることができます。

そのために、HTTPリクエストを処理するコントローラーを利用します。

ProductsControllerを作成しましょう:

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

この実装により、ActiveWebはindex()メソッドを次のURIに自動的にマップします

//:/products

@RESTfulアノテーションが付けられたコントローラーはさまざまなURIに自動的にマップされるメソッドの固定セットを提供します。CRUDの例に役立つものを見てみましょう。

コントローラ方式 HTTPメソッド URI
CREATE create() 役職 // host:port / products
1つ読む 公演() 取得する // host:port / products / {id}
すべて読む index() 取得する // host:port / products
更新 更新() プット // host:port / products / {id}
削除 破壊() 削除 // host:port / products / {id}

そして、この一連のメソッドを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 } }

ロジックの実装に移る前に、構成する必要のあるいくつかの事項について簡単に説明します。

5.構成

ActiveWebは主に規則に基づいており、プロジェクト構造はその一例です。ActiveWebプロジェクトは、事前定義されたパッケージレイアウトに従う必要があります

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

確認する必要のある特定のパッケージが1つあります。それはpp.configです。

そのパッケージ内に、次の3つのクラスを作成します。

public class DbConfig extends AbstractDBConfig { @Override public void init(AppContext appContext) { this.configFile("/database.properties"); } }

このクラスは、必要なパラメータを含むプロジェクトのルートディレクトリにあるプロパティファイル使用してデータベース接続構成します

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

これにより、マッピングテストの最初の行で行ったことを自動的に置き換える接続が作成されます。

app.configパッケージ内に含める必要がある2番目のクラスは次のとおりです。

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

このコードは、構成したばかりの接続をコントローラーにバインドします。

The third class willconfigure our app's context:

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

After creating the three classes, the last thing regarding configuration is creating our web.xml file under webapp/WEB-INF directory:

   dispatcher org.javalite.activeweb.RequestDispatcher  exclusions css,images,js,ico   encoding UTF-8    dispatcher /*  

Now that configuration is done, we can go ahead and add our logic.

6. Implementing CRUD Logic

With the DAO-like capabilities provided by our Product class, it's super simple to add basic CRUD functionality:

@RESTful public class ProductsController extends AppController { private ObjectMapper mapper = new ObjectMapper(); public void index() { List 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(); // ... } }

Easy, right? However, this isn't returning anything yet. In order to do that, we have to create some views.

7. Views

ActiveWeb uses FreeMarker as a templating engine, and all its templates should be located under src/main/webapp/WEB-INF/views.

Inside that directory, we will place our views in a folder called products (same as our controller). Let's create our first template called _product.ftl:

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

It's pretty clear at this point that this is a JSON response. Of course, this will only work for one product, so let's go ahead and create another template called index.ftl:

[]

This will basically render a collection named products, with each one formatted by _product.ftl.

Finally, we need to bind the result from our controller to the corresponding view:

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

In the first case, we're assigning products list to our template collection named also products.

Then, as we're not specifying any view, index.ftl will be used.

In the second method, we're assigning product p to element product in the view and we're explicitly saying which view to render.

We could also create a view message.ftl:

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

And then call it form any of our ProductsController‘s method:

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

Let's now see our final 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; } }

At this point, our application is done and we're ready to run it.

8. Running the Application

We'll use Jetty plugin:

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

Find latest jetty-maven-plugin in Maven Central.

And we're ready, we can run our application:

mvn jetty:run

Let's create a couple of products:

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

.. read them:

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

..それらの1つを更新します:

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

…更新したばかりのものを読んでください。

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

最後に、1つを削除できます。

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

9.結論

JavaLiteには、開発者がアプリケーションを数分で起動して実行するのに役立つツールがたくさんあります。ただし、規則に基づいてコードを作成すると、コードがよりクリーンでシンプルになりますが、クラス、パッケージ、およびファイルの名前と場所を理解するには時間がかかります。

これはActiveWebとActiveJDBCの紹介にすぎませんでした。彼らのウェブサイトでより多くのドキュメントを見つけ、Githubプロジェクトで当社の製品アプリケーションを探してください。