Удалёнка: опыт и лайфхаки
7 февраля

Пример реализации универсального REST сервиса (Avalanche — application framework for Java)

Высокая производительностьJavaСофт

Пример реализации универсального REST сервиса (Avalanche — application framework for Java)


"Avalanche — application framework for Java" — реализация технологии стирающей различия
между вызовами локального и удаленного кода. Отказоустойчивость, масштабируемость,
модифицируемость, непрерывная доступность идут в комплекте приятными бонусами.


В статье описан пример реализации универсального REST сервиса. В основу идеи реализации сервиса положено утверждение, что FrontEnd в каждый момент времени "знает", какими данными он манипулирует и что собирается запросить у BackEnd-а. Это утверждение позволило отказаться от разработки ORM в BackEnd-е и использования JPA при реализации универсального сервиса. Сервис способен манипулировать содержимым любой таблицы БД. Изменения структуры или созданные объекты БД сразу становятся доступны методам сервиса.


В сервисе реализованы методы трех групп:


  • .../data/table/… — манипулирования содержимым таблиц БД;
  • .../data/query/… — выполнение произвольных запросов, расширяющих функциональность сервиса;
  • .../data/info/… — получения информации о структуре объектов БД.

В сервисе реализована система ошибок, которая однозначно позволяет понять причину и место возникновении ошибки. По умолчанию, поддерживается локализация сообщений ru и en. Не все сообщения могут быть локализованы, например сообщения ошибок выполнения SQL запросов, которые зависят от локализации среды выполнения.


Сервис поддерживает работу с данными в форматах XML и JSON.


Сервис реализован с использованием библиотеки Jersey 2.0.


В общем виде формат URL строки запроса сервиса выглядит так


http://host:port/name_context/map_servlet/data/type_group/{object}[?request_parameters]

[request_body]

Где:


  • name_context — имя контекста (WEB приложения);
  • map_servlet — имя маппирования сервлета Jersey в файле web.xml;
  • data — путь этого сервиса
  • type_group — тип группы запросов (table, query или info);
  • {object} — параметр запроса

Параметр запроса {object} может принимать значения:


  • для типа table — имя_схемы.имя_таблицы в формате schema.table;
  • для типа query — имя_класса.имя_метода в формате class.method, имя класса указывается без пакета, поиск класса осуществляется в пакетах, указанных в параметре packages;
  • для типа info — имя_метода в формате method;

request_parameters — параметры запроса, могут отсутствовать


request_body — тело запроса, может отсутствовать


В HTTP заголовке могут указываться параметры:


  • Accept — не обязательный, формат данных, ожидаемый в ответе на запрос, может принимать значения application/json (по умолчанию) или application/xml;
  • Content-Language — не обязательный, язык локализации, может принимать значения ru (по умолчанию) или en;
  • Content-Type — обязательный, при наличии тела запроса, может принимать значения application/json или application/xml.

Реализация сервиса


Реализация сервиса TableService.java опубликована на GitHub.


Пример конфигурационного файла сервиса avalanche-restdb-config.xml


Пример конфигурационного файла контекста restdb.xml


Ниже приведены примеры различных запросов


Примеры запросов


Пример получения записи таблицы rp.structure_type по значению первичного ключа


Запрос:


GET /restdb/rs/data/table/rp.structure_type?st_id=43 HTTP/1.1
Accept: application/json
Content-Language: ru
Host: localhost:8080

Ответ: (HTTP Status — 200)


[
{
"st_id": 43,
"st_name": "Test43 "
}
]

Пример получения нескольких записей таблицы rp.structure_type по значению первичного ключа


Запрос:


GET /restdb/rs/data/table/rp.structure_type?st_id=43&st_id=42 HTTP/1.1
Accept: application/json
Content-Language: ru
Host: localhost:8080

Ответ: (HTTP Status — 200)


[
{
"st_id": 42,
"st_name": "Test42 "
},
{
"st_id": 43,
"st_name": "Test43 "
}
]

Пример получения всех записей таблицы rp.users, если это не запрещено в конфигурации сервиса


Запрос:


GET /restdb/rs/data/table/rp.users HTTP/1.1
Accept: application/json
Content-Language: ru
Host: localhost:8080

Ответ: (HTTP Status — 200)


[
{
"us_last": "Петров ",
"us_name": "Петр ",
"us_email": "p_petrov@domain.ru "
},
{
"us_last": "Сидоров ",
"us_name": "Сидор ",
"us_email": "s_sidorov@domain.ru "
},
{
"us_last": "Пупкин ",
"us_name": "Пуп ",
"us_email": "p_pupkin@domain.ru "
},
{
"us_last": "Остапов ",
"us_name": "Остап ",
"us_email": "o.ostapov@domain.ru "
},
{
"us_last": "Иванов ",
"us_name": "Иван ",
"us_email": "i.ivanov@domain.ru "
},
{
"us_last": "Кириллов ",
"us_name": "Кирилл ",
"us_email": "k.kirillov@domain.ru "
}
]

Пример получения записей таблицы rp.users, имеющих вхождение подстроки "ров" в поле us_last


Запрос:


GET /restdb/rs/data/table/rp.users?us_name=%ров% HTTP/1.1
Accept: application/json
Content-Language: ru
Host: localhost:8080

Ответ: (HTTP Status — 200)


[
{
"us_last": "Петров ",
"us_name": "Петр ",
"us_email": "p_petrov@domain.ru "
},
{
"us_last": "Сидоров ",
"us_name": "Сидор ",
"us_email": "s_sidorov@domain.ru "
}
]

Пример вставить одну запись в таблицу rp.test


Если в теле запроса будет указано несколько записей вставятся в таблицу множество записей.


Запрос:


POST /restdb/rs/data/table/rp.test HTTP/1.1
Accept: application/json
Content-Language: ru
Content-Length: 58
Host: localhost:8080
Content-Type: application/json

[{"ts_id":9, "ts_tszone":"2019-01-19T15:37:13.380+03:00"}]

Ответ: (HTTP Status — 200)


{
"query": "INSERT",
"result": 1,
"timer": 3
}

Пример вставить одну запись в таблицу rp.test


Пример, аналогичный запросу выше, но с указанием параметров вставляемой записи в строке запроса. Если в строке запроса присутствуют параметры то тело запроса в HTTP методе POST игнорируется.


Запрос:


POST /restdb/rs/data/table/rp.test?ts_id=10&ts_tszone=2019-01-19T15:37:13.380+03:00 HTTP/1.1
Accept: application/json
Content-Language: ru
Content-Length: 58
Host: localhost:8080
Content-Type: application/json

Ответ: (HTTP Status — 200)


{
"query": "INSERT",
"result": 1,
"timer": 3
}

Пример неудачной попытки вставить запись с использование метода insert класса SQL генератора TestQuery


В теле запроса допущена опечатка (намеренно), в имени поля us_last пропущена буква "s"


Запрос:


POST /restdb/rs/data/query/TestQuery.insert HTTP/1.1
Accept: application/xml
Content-Language: ru
Content-Length: 61
Host: localhost:8080
Content-Type: application/json

[{"us_name":"3", "us_lat":"333", "us_email":"333@domain.ru"}]

Ответ: (HTTP Status — 400)


<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<serverError>
<cause>
[24344@likhovskikh-vv] SYS0250E При вызове метода "execute" возникла ошибка.
Ошибка: "ru.funsys.avalanche.sql.SQLException: [24344@likhovskikh-vv] SQL0021E При выполнении запроса произошла ошибка. Номер запроса: 0, запрос: INSERT INTO rp.users (us_name, us_last, us_email) VALUES (?, ?, ?).&#xD;
org.postgresql.util.PSQLException: ОШИБКА: нулевое значение в столбце "us_last" нарушает ограничение NOT NULL
Подробности: Ошибочная строка содержит (3 , null, 333@domain.ru )."
</cause>
<cоde>RST0007E</cоde>
<message>При выполнение SQL запроса произошла ошибка.</message>
</serverError>

Преимущества универсального сервиса по сравнению с общепринятым подходом реализации REST сервисов.


  1. Универсальный REST сервис готов для использования с любой реляционной БД.
  2. Нет зависимости от требования JPA обязательного наличия индексного идентификатора ID в каждой таблице модели данных. Коллеги, в реляционной теории БД нет понятия индексного поля таблицы ID, которое используется в качестве первичного ключа. Наличия этого поля превращает вашу реляционную БД в индексную.
  3. URI, параметры запроса, тело запроса и результат выполнения запроса формализованы.
  4. Универсальный сервис легко расширяемый, достаточно определить требуемые методы генерации SQL запросов в одном или нескольких классах SQL генераторов.
  5. Первичный ключ таблиц БД может состоять из множества полей. Поля первичного ключа доступны для модификации при соблюдении условий целостности.
  6. Отсутствие отображения программной модели объектов модели данных в БД значительно упрощает эксплуатацию, особенно в распределенной среде (например, в системах с георезервированием во множестве ЦОД).

Другие статьи по данной теме


  1. Модель приложения (Avalanche — application framework for Java)
  2. Первое приложение (Avalanche — application framework for Java)
Теги: Avalanche MVFA
Хабы: Высокая производительность Java Софт
+5
4,3k 34
Комментарии 235
Реклама

Рекомендуем