Рубрики
Без рубрики

Руководство по созданию JavaLite – спокойного приложения CRUD

Узнайте о платформе JavaLite, которая упрощает общие задачи, с которыми приходится сталкиваться каждому разработчику при создании приложений.

Автор оригинала: baeldung.

1. введение

Java Lite представляет собой набор фреймворков для упрощения общих задач , с которыми приходится сталкиваться каждому разработчику при создании приложений.

В этом уроке мы рассмотрим функции Java Lite, ориентированные на создание простого API.

2. Настройка

На протяжении всего этого урока мы создадим простое приложение RESTful CRUD. Для этого мы будем использовать ActiveWeb и ActiveJDBC – две платформы, с которыми интегрируется JavaLite.

Итак, давайте начнем и добавим первую зависимость, которая нам нужна:


    org.javalite
    activeweb
    1.15

Активный веб-артефакт включает ActiveJDBC, поэтому нет необходимости добавлять его отдельно. Пожалуйста, обратите внимание, что последнюю версию active web можно найти в Maven Central.

Вторая зависимость, которая нам нужна, – это соединитель базы данных . Для этого примера мы будем использовать MySQL, поэтому нам нужно добавить:


    mysql
    mysql-connector-java
    5.1.45

Опять же, последнюю версию mysql-connector-java зависимости можно найти в Maven Central.

Последняя зависимость, которую мы должны добавить, относится к Java Lite:


    org.javalite
    activejdbc-instrumentation
    1.4.13
    
        
            process-classes
            
                instrument
            
        
    

Последний плагин activejdbc-instrumentation также можно найти в Maven Central.

Имея все это на месте и перед началом работы с сущностями, таблицами и сопоставлениями, мы убедимся, что одна из поддерживаемых баз данных запущена и работает . Как мы уже говорили, мы будем использовать MySQL.

Теперь мы готовы начать с объектно-реляционного отображения.

3. Объектно-Реляционное сопоставление

3.1. Картографирование и инструментирование

Давайте начнем с создания Продукта класса, который будет нашей основной сущностью :

public class Product {}

И, давайте также создадим для него соответствующую таблицу :

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

Наконец, мы можем изменить наш Продукт класс, чтобы выполнить сопоставление :

public class Product extends Model {}

Нам нужно только расширить org.javalite.activejdbc.Модель класс. ActiveJDBC выводит параметры схемы БД из базы данных . Благодаря этой возможности нет необходимости добавлять геттеры и сеттеры или какие-либо аннотации .

Кроме того, ActiveJDBC автоматически распознает, что Продукт класс необходимо сопоставить с ПРОДУКТАМИ таблицей. Он использует английские флексии для преобразования формы единственного числа модели в форму множественного числа таблицы. И да, это работает и с исключениями.

Есть еще одна последняя вещь, которая нам понадобится для того, чтобы наше картографирование работало: приборы. Инструментирование-это дополнительный шаг, необходимый ActiveJDBC , который позволит нам играть с нашим Продуктом классом, как если бы у него были геттеры, сеттеры и методы, подобные DAO.

После запуска инструментов мы сможем делать такие вещи, как:

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

или:

List products = Product.findAll();

Именно здесь появляется activejdbc-инструментарий плагин. Поскольку у нас уже есть зависимость в нашем 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. Тестирование

Наконец, чтобы протестировать наше сопоставление, мы выполним три простых шага: откроем соединение с базой данных, сохраним новый продукт и извлекем его:

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

}

С помощью этой реализации Active Web автоматически сопоставит метод index() со следующим URL-адресом:

http://:/products

Контроллеры с аннотациями @RESTful , предоставляют фиксированный набор методов, автоматически сопоставляемых с различными URI. Давайте посмотрим, какие из них будут полезны для нашего примера CRUD:

ПОСТ http://host:port/products создать() ТВОРИТЬ
ПОЛУЧИТЬ http://host:port/products/{идентификатор} показать() ПРОЧТИТЕ ОДИН
ПОЛУЧИТЬ http://host:port/products индекс() ПРОЧИТАЙТЕ ВСЕ
КЛАСТЬ http://host:port/products/{идентификатор} обновление() ОБНОВЛЕНИЕ
УДАЛИТЬ http://host:port/products/{идентификатор} уничтожить() УДАЛИТЬ

И если мы добавим этот набор методов в наш 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 основан в основном на соглашениях, структура проекта является примером этого. Активные веб-проекты должны следовать предопределенному макету пакета :

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

Есть один конкретный пакет, на который нам нужно взглянуть – a pp.config .

Внутри этого пакета мы собираемся создать три класса:

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 пакет, – это:

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

Этот код свяжет соединение, которое мы только что настроили, с нашим контроллером.

Третий класс будет настраивать контекст вашего приложения :

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

После создания трех классов последнее, что касается конфигурации, – это создание вашего web.xml файл в веб-приложение/WEB-INF каталог:




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

    
        dispatcher
        /*
    

Теперь, когда настройка завершена, мы можем продолжить и добавить нашу логику.

6. Реализация логики CRUD

Благодаря возможностям, подобным DAO, предоставляемым нашим Продуктом классом, очень просто добавить базовую функциональность CRUD :

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

Легко, правда? Однако это еще ничего не возвращает. Для этого нам нужно создать несколько представлений.

7. Просмотры

Активные веб-пользователи Фримаркер как механизм создания шаблонов, и все его шаблоны должны располагаться под src/главная/веб-приложение/ВЕБ-ИНФОРМАЦИЯ/просмотры .

Внутри этого каталога мы разместим наши представления в папке с именем products (такой же, как у нашего контроллера). Давайте создадим наш первый шаблон под названием _product.ftl :

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

На данный момент довольно ясно, что это ответ JSON. Конечно, это будет работать только для одного продукта, поэтому давайте продолжим и создадим еще один шаблон под названием index.ftl :

[<@render partial="product" collection=products/>]

Это в основном приведет к отображению коллекции с именем продукты , причем каждый из них отформатирован , причем каждый из них отформатирован .

Наконец, нам нужно привязать результат от нашего контроллера к соответствующему представлению :

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

В первом случае мы присваиваем products список нашей коллекции шаблонов с именем также products .

Затем, поскольку мы не указываем никакого представления, будет использоваться index.ftl .

Во втором методе мы назначаем product p элементу product в представлении и явно указываем, какое представление отображать.

Мы также могли бы создать представление message.ftl :

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

А затем вызовите его для многих наших ProductsController методов:

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

Давайте теперь посмотрим наш окончательный 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;
    }
}

На данный момент наше приложение готово, и мы готовы его запустить.

8. Запуск приложения

Мы будем использовать плагин Jetty:


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

Найдите последнюю версию jetty-maven-плагина в Maven Central.

И мы готовы, мы можем запустить наше приложение :

mvn jetty:run

Давайте создадим пару продуктов:

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

.. прочитайте их:

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

.. обновите один из них:

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

… прочитайте ту, которую мы только что обновили:

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

Наконец, мы можем удалить один:

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

9. Заключение

В Java Lite есть множество инструментов, которые помогут разработчикам запустить приложение за считанные минуты . Однако, хотя на основе соглашений получается более чистый и простой код, требуется некоторое время, чтобы понять имена и расположение классов, пакетов и файлов.

Это было только введение в Active Web и ActiveJDBC, найдите дополнительную документацию на их веб-сайте и найдите приложение для наших продуктов в проекте Github .