Загрузка сущностей
Entities API позволяет различными способами загружать сущности через API:
-
Загрузка сущности по ID загружает одну сущность по ее уникальному идентификатору (ID).
-
Загрузка списка сущностей загружает все сущности с разбивкой на страницы и сортировкой.
-
Загрузка сущностей через фильтр поиска позволяет фильтровать сущности по критериям фильтрации в запросе.
-
Загрузка сущностей через JPQL загружает сущности с помощью предварительной настройки именованных запросов JPQL.
Загрузка сущности по ID
Первый способ загрузки объекта через Entities API – это загрузка по идентификатору. Соответствующей для этого операцией является конечная точка Load Entity /entities/:entityName/:entityId
.
Параметр пути :entityName
определяет тип сущности. Значение задается в определении сущности:
@JmixEntity
@Table(name = "SAMPLE_ORDER")
@Entity(name = "sample_Order") (1)
public class Order {
// ...
}
1 | Значение атрибута имени sample_Order аннотации JPA @Entity указывает параметр entityName в Entities API. |
Операция Load Entity возвращает один экземпляр, если он найден по своему идентификатору. В противном случае возвращается код состояния HTTP 404 - Not Found
.
GET http://localhost:8080/rest
/entities
/sample_Order
/21021f78-edac-224b-e6f8-6e71e02a0f0d
{
"_entityName": "sample_Order", (1)
"_instanceName": "rest.sample.entity.Order-21021f78-edac-224b-e6f8-6e71e02a0f0d [detached]",
"id": "21021f78-edac-224b-e6f8-6e71e02a0f0d",
"date": "2020-12-13", (2)
"amount": 49.99,
"createdDate": "2021-02-06T12:03:38.049",
"createdBy": "admin",
"lastModifiedDate": "2021-02-06T12:03:38.049",
"version": 1
}
1 | Некоторые метаданные об экземпляре сущности возвращаются в виде ключей JSON (entityName , _instanceName и id ). |
2 | Объект JSON содержит каждый бизнес-атрибут в качестве ключа. |
Некоторые атрибуты сущности Если вы хотите включить в ответ динамические атрибуты, используйте параметр
|
Использование фетч-планов
Необходимые атрибуты обычно зависят от варианта использования клиента, пользовательского интерфейса или сценария интеграции. Часто загрузки только прямых атрибутов сущности недостаточно.
Если каждый тип сущности возможно загружать только в отдельном запросе, в последующих запросах потребовалось бы загружать информацию о сущности, на которую дана ссылка. Это привело бы к проблемам с запросами N+1 во всем приложении. В частности, при взаимодействии через HTTP такая нагрузка может стать огромной.
Поэтому Load Entities API также поддерживает использование фетч-планов. С помощью них можно настроить дерево атрибутов, которые должны загружаться из базы данных вместе, одним пакетом, и передаваться клиенту через Load Entities API.
В следующем примере рассмотрим, как загрузить список заказов (Order) с дополнительной информацией о соответствующих клиентах (Customer), связанных строках заказа и даже информацией о продукте соответствующей линейки.
Во-первых, необходимо зарегистрировать фетч-план order-with-details
в файле конфигурации fetch-plans.xml
:
<fetchPlans xmlns="http://jmix.io/schema/core/fetch-plans">
<fetchPlan class="rest.sample.entity.Order"
extends="_base"
name="order-with-details">
<property name="customer"/>
<property name="lines" fetchPlan="_base">
<property name="product" fetchPlan="_instance_name" />
</property>
</fetchPlan>
</fetchPlans>
Имея эту конфигурацию, вы можете выполнить запрос и сослаться на фетч-план через параметр URL fetchPlan
.
В приведенном ниже примере Order с идентификатором 21021f78-edac-224b-e6f8-6e71e02a0f0d
загружается с планом выборки order-with-details
, чтобы дополнительно загрузить данные customer
и lines
:
GET http://localhost:8080/rest
/entities
/sample_Order
/21021f78-edac-224b-e6f8-6e71e02a0f0d
?fetchPlan=order-with-details
{
"id": "21021f78-edac-224b-e6f8-6e71e02a0f0d",
"date": "2020-12-13",
"amount": 49.99,
"lines": [ (1)
{
"id": "64e4fbb0-7fd6-818b-984e-a8769c4fbe88",
"product": {
"id": "7750adbe-6c30-cede-31a6-577a1a96aa83",
"name": "Outback Power Remote Power System"
},
"quantity": 1.0
}
],
"version": 1,
"customer": {
"id": "0826806e-6074-90fa-f241-564b5c94d018",
"name": "Sidney Chandler",
}
}
1 | Фетч-план order-with-details гарантирует, что включены дополнительные атрибуты, такие как lines и customer . |
Загрузка списка сущностей
Вы можете загрузить список сущностей любого типа, используя операцию Load Entity List API: /entities/:entityName
. Этот API включает в себя разбивку на страницы, сортировку и фетч-планы.
GET http://localhost:8080/rest/entities/sample_Customer
[
{
"id": "0826806e-6074-90fa-f241-564b5c94d018",
"name": "Sidney Chandler"
},
{
"id": "22efc597-69a9-aeef-4e4a-7afccd8e5767",
"name": "Randall Bishop"
},
{
"id": "bd1c8e90-3d35-cbe2-9efd-167202c758d2",
"name": "Shelby Robinson"
}
]
Каждая сущность в ответе имеет атрибут _entityName с именем сущности и атрибут _instanceName с именем экземпляра сущности.
|
Также можно дополнительно проконтролировать поведение API, используя следующие параметры URL-запроса:
- dynamicAttributes
-
следует ли загружать динамические атрибуты для сущности
(Boolean)
. - fetchPlan
-
имя фетч-плана
(String)
. - limit
-
количество сущностей, которое должен вернуть API
(int)
. - offset
-
позиция первой возвращенной сущности
(int)
. - sort
-
атрибут сущности, которая будет использоваться для сортировки
(String)
.-
+attribute
или простоattribute
для порядка по возрастанию. -
-attribute
для порядка по убыванию.
-
Использование сортировки
Load Entities API поддерживает сортировку результата по атрибутам сущности. Для управления порядком сущностей используется параметр URL sort
.
Если параметр sort не указан, порядок сортировки по умолчанию зависит от реализации базы данных. Обычно базы данных сортируют по времени создания записи, но это поведение не гарантировано и может отличаться в разных ситуациях.
|
Jmix имеет специальный синтаксис для определения порядка сортировки. Порядок по возрастанию выражается через +
перед именем атрибута, что необязательно, так как это поведение по умолчанию. Для порядка по убыванию необходимо поставить перед атрибутом сущности символ -
.
В следующем примере показано, как можно отсортировать клиентов (Customer) по возрастанию их атрибутов name
.
GET http://localhost:8080/rest
/entities
/sample_Customer
?sort=name
[
{
"id": "d83c9d66-cb23-075a-8d3c-d4035d338705",
"name": "Klaudia Kleinert"
},
{
"id": "8985ba1e-1cc8-eb5c-f9e0-738aee9d2ef1",
"name": "Randall Bishop"
}
]
Можно также сортировать по нескольким атрибутам. В этом случае порядок сортировки принимает список атрибутов, разделенных запятыми.
GET http://localhost:8080/rest
/entities
/sample_Order?sort=+date,-amount
[
{
"id": "41aae331-b46b-85ee-b0bc-2de8cbf1ab86",
"date": "2021-02-02", (1)
"amount": 283.55
},
{
"id": "288a5d75-f06f-d150-9b70-efee1272b96c",
"date": "2021-03-01",
"amount": 249.99, (2)
"lastModifiedBy": "admin"
},
{
"id": "1068c217-5868-faf4-16aa-23655e9492da",
"date": "2021-03-01",
"amount": 130.08
}
]
1 | Первым возвращается результат с самой старой датой. |
2 | Когда атрибут date одинаков, для сортировки результатов используется amount . |
Использование разбивки на страницы
Entities API поддерживает разбивку на страницы для соблюдения ограничений обработки данных, которые могут присутствовать на стороне сервера или клиента. Если вы хотите загрузить только определенное подмножество сущностей, можно указать параметры URL offset
и limit
.
Разбивка на страницы активна по умолчанию, даже если она не запрошена клиентом явно. Если в запросе не указано значение Это значение по умолчанию настраивается глобально в jmix.rest.default-max-fetch-size или для каждого сущности отдельно в jmix.rest.entityMaxFetchSize. |
В следующем примере показано, как загрузить третью страницу, содержащую две сущности Customer
(5. и 6.):
GET http://localhost:8080/rest
/entities
/sample_Customer
?limit=2
&offset=4
&sort=createdDate
[
{
"id": "2d620164-1e80-0696-c3aa-45b7b5c81f2c",
"name": "Maria Mitchell"
},
{
"id": "3c7ec69d-9b85-c6e9-387b-42a5bccb79de",
"name": "Anthony Knutson"
}
]
Загрузка сущностей через фильтр поиска
Вы можете указать критерии фильтрации при загрузке сущностей с помощью операции Entity Search: /entities/:entityName/search
.
При взаимодействии с операцией поиска возможно использовать оба метода HTTP GET
и POST
. В обоих случаях критерий фильтрации должен быть предоставлен как часть запроса.
Определение фильтра — это структура JSON, содержащая набор условий. Условие состоит из следующих атрибутов:
- property
-
атрибут объекта, по которому проводится фильтрация (например,
amount
сущности Order).Если атрибут является ссылкой на другую сущность, он также может быть путем к свойству, например
customer.name
- operator
-
оператор фильтра. Оператор описывает, как фильтровать определенный атрибут. Существует несколько операторов, которые можно использовать независимо от типа данных:
-
Стандартные операторы:
=
,<>
,notEmpty
,isNull
-
Операторы списка:
in
,notIn
-
Кроме того, некоторые операторы возможны только для определенных типов данных:
Datatype | Специальные операторы |
---|---|
String, UUID |
|
Integer, Long, Double, BigDecimal, Date, DateTime, Time, LocalDate, LocalDateTime, LocalTime, OffsetDateTime, OffsetTime |
|
- value
-
значение для поиска. Не требуется для операторов
notEmpty
иisNull
.
Помимо этого условия можно комбинировать с помощью групповых условий AND
и OR
для определения более сложного критерия фильтрации. JSON-структура определений фильтров выглядит следующим образом:
{
"conditions": [
{
"group": "OR",
"conditions": [
{
"property": "stringField",
"operator": "=",
"value": "stringValue"
},
{
"property": "intField",
"operator": ">",
"value": 100
}
]
},
{
"property": "booleanField",
"operator": "=",
"value": true
}
]
}
Это представление критерия фильтра ((stringField = stringValue) OR (intField > 100) AND (booleanField = true))
.
При использовании метода HTTP POST
фильтр является частью тела запроса.
POST http://localhost:8080/rest/entities/sample_Order/search
{
"filter": {
"conditions": [
{
"property": "customer.name",
"operator": "=",
"value": "Shelby Robinson"
}
]
}
}
При использовании метода GET
критерий фильтра JSON необходимо передавать через параметр URL-запроса filter
.
GET http://localhost:8080/rest
/entities
/sample_Order
/search
?filter={"conditions":[{"property":"customer.name","operator":"contains","value":"Shelby"}]}
Кодировка URI
Стандарт HTTP URI допускает символы ASCII только как часть URI/URL. При использовании параметров URL-запроса для определения фильтра определение JSON должно быть URL-закодировано, чтобы соответствовать этому требованию. Это также верно в случае данных Практическое ограничение длины URI может оказаться проблемой для больших определений фильтров, поэтому метод фильтрации сущностей |
Загрузка сущностей через JPQL
Другой альтернативой загрузке сущностей из приложения является использование предопределенных запросов JPQL. За предоставление этой возможности отвечает операция Entity Query /queries/:entityName/:queryName
. Запросы могут содержать список параметров, которые должен предоставить клиент. Кроме того, конечная точка содержит все те же общие параметры для разбивки на страницы, фетч-планы и т. д.
Когда использовать JPQL вместо фильтра поиска?
Jmix предоставляет различные способы общей загрузки данных сущности. Используйте предопределенные запросы JPQL, если фильтр поиска недостаточно продвинут для выражения критерия фильтра, а также в случае, если параметр должен быть предопределен и не может быть изменен клиентом API. |
Конфигурация запроса JPQL
Чтобы использовать операцию Entity Query, необходимо определить доступные запросы через файл конфигурации XML, обычно называемый rest-queries.xml
. Создайте этот новый файл в вашем приложении Jmix в src/main/resources
. В нем перечисляются все опубликованные запросы с информацией об их параметрах.
<?xml version="1.0"?>
<queries xmlns="http://jmix.io/schema/rest/queries">
<query name="ordersByDate" entity="sample_Order" fetchPlan="order-with-details">
<jpql><![CDATA[select e from sample_Order e where e.date = :orderDate]]></jpql>
<params>
<param name="orderDate" type="java.time.LocalDate"/>
</params>
</query>
<query name="ordersByCustomerName" entity="sample_Order" fetchPlan="order-with-details">
<jpql><![CDATA[select e from sample_Order e where e.customer.name = :customerName]]></jpql>
<params>
<param name="customerName" type="java.lang.String"/>
</params>
</query>
</queries>
Запрос должен иметь уникальное значение name
, а также ссылку entity
. Сочетание name
и entity
должно быть уникальным. Кроме того, необходима ссылка на fetchPlan
, чтобы указать возвращаемые атрибуты сущности.
В теге <jpql>
конфигурируется сам запрос. Параметры должны быть перечислены внутри тега params
, определяющего их имя и тип Java. В параметрах запроса можно ссылаться через их имя с префиксом двоеточия, например :customerName
.
После создания файла и определения запросов необходимо зарегистрировать конфигурацию rest-queries.xml
в application.properties
вашего приложении Jmix:
jmix.rest.queries-config = rest/sample/rest-queries.xml
Операцию Entity Query можно вызвать с помощью HTTP метода GET
или POST
. В случае выбора GET
параметры добавляются как параметры URL-запроса.
GET http://localhost:8080/rest
/queries
/sample_Order
/ordersByDate
?orderDate=2020-02-02
URI-Кодировка
URL-адрес должен содержать только символы ASCII. Это означает, что значения параметров должны быть URL-закодированы, поскольку эти значения обычно представляют собой прямой пользовательский ввод, и гарантировать использование отличных от ASCII символов невозможно. |
В случае использования POST
параметры запроса передаются в теле JSON, содержащем каждый параметр в качестве ключа.
POST http://localhost:8080/rest/queries/sample_Order/ordersByCustomerName
{
"customerName": "Shelby Robinson"
}
Параметры коллекции
Также возможно определить параметр как тип коллекции. В этом случае определение запроса должно содержать индикатор []
после типа Java.
<?xml version="1.0"?>
<queries xmlns="http://jmix.io/schema/rest/queries">
<query name="ordersByIds" entity="sample_Order" fetchPlan="order-with-details">
<jpql><![CDATA[select e from sample_Order e where e.id in :ids]]></jpql>
<params>
<param name="ids" type="java.util.UUID[]"/> (1)
</params>
</query>
</queries>
1 | Параметр ids помечен как коллекция типа UUID . |
Когда этот параметр используется в запросе, соответствующие идентификаторы должны быть предоставлены в виде массива JSON.
POST http://localhost:8080/rest/queries/sample_Order/ordersByIds
{
"ids": [
"41aae331-b46b-85ee-b0bc-2de8cbf1ab86",
"21021f78-edac-224b-e6f8-6e71e02a0f0d"
]
}
Возврат пустых значений в JSON
По умолчанию Jmix удаляет пустые значения (null
) из ответа JSON, чтобы ключи атрибутов не присутствовали в документе JSON.
Вы можете управлять этим поведением, используя параметр URL-запроса returnNulls
и присвоив ему значение true
. Тогда Jmix будет всегда добавлять ключи атрибутов к ответу, независимо от того, является ли значение пустым или нет.
В следующем примере Customer
загружается по его идентификатору, а также запрашиваются все пустые значения:
GET http://localhost:8080/rest
/entities
/sample_Customer
/1eab4973-25f9-70d9-5356-6990dd8f79e2
?returnNulls=true
{
"_entityName": "sample_Customer",
"_instanceName": "Sidney Chandler",
"id": "0826806e-6074-90fa-f241-564b5c94d018",
"createdDate": "2021-06-09T08:42:39.291",
"createdBy": "admin",
"lastModifiedDate": "2021-06-09T08:42:39.291",
"deletedDate": null,
"lastModifiedBy": null,
"name": "Sidney Chandler",
"type": null, (1)
"version": 1,
"deletedBy": null
}
1 | Ответ содержит ключ type , хотя тот пуст |
Параметр returnNulls присутствует во всех Entity Load API: Загрузка по ID, Загрузка списка, Поиск и загрузка по запросу.
|