Load Entities

The Entities API allows you to load entities through the API in various ways:

Load Entity by ID

The first way of loading an entity via the Entities API is by loading it via its ID. The corresponding endpoint for this is the Load Entity endpoint /entities/:entityName/:entityId.

The :entityName path parameter defines which entity type it is. The value is defined in the entity definition:

Order.java
@JmixEntity
@Table(name = "SAMPLE_ORDER")
@Entity(name = "sample_Order") (1)
public class Order {
    // ...
}
1 The name attribute value sample_Order of the @Entity JPA annotation indicates the entityName parameter in the Entities API.

The Load Entity endpoint returns a single instance in case it is found by its identifier. Otherwise, HTTP status code 404 - Not Found is returned.

Load Order Request
GET http://localhost:8080/rest
            /entities
            /sample_Order
            /21021f78-edac-224b-e6f8-6e71e02a0f0d
Response: 200 - OK
{
  "_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 Some metadata about the entity instance is returned as JSON keys (entityName, _instanceName and id).
2 The JSON Object contains each business attribute as a key.

Some attributes of the Order entity are missing in the JSON, as by default only local and persistent attributes of the entity are loaded. In case of reference attributes should also be included in the response you need to use Fetch Plans as described below.

In case you want to include dynamic attributes in the response, use the dynamicAttributes parameter to add a special hint to the query:

GET http://localhost:8080/rest
            /entities
            /sample_Order
            /21021f78-edac-224b-e6f8-6e71e02a0f0d
            ?dynamicAttributes=true

Using Fetch Plans

Which attributes are needed normally varies based on the clients' use-case, UIs, or integration scenario. Loading only the direct attributes of an entity is oftentimes not enough.

With only the ability to load each entity type in a dedicated request, it would require loading referenced entity information in subsequent requests. This would lead to N+1 query problems in the overall application. In particular, when interacting through HTTP, the corresponding overhead can be tremendous.

Therefore, the concept of Fetch Plans is also supported in the Load Entities API. With Fetch Plans you can configure a tree of attributes that should be loaded all in one batch from the database and transferred through the Load Entities API to the client.

The following example demonstrates how to load a list of Orders with additional information about the corresponding customer, the linked order lines, and even information about the product of that product line.

First, you need to register the fetch plan order-with-details in the fetch-plans.xml configuration file:

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>

With that fetch plan configuration in place, you can perform the request and reference the fetch plan through the fetchPlan URL parameter.

In the example below the Order with the ID 21021f78-edac-224b-e6f8-6e71e02a0f0d is loaded with the fetch plan order-with-details in order to additionally load customer and lines data:

Load Order with Fetch Plan Request
GET http://localhost:8080/rest
            /entities
            /sample_Order
            /21021f78-edac-224b-e6f8-6e71e02a0f0d
            ?fetchPlan=order-with-details
Response: 200 - OK
{
  "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 The fetch plan order-with-details ensures that additional attributes like lines and customer are also included.

Load List of Entities

You can load a list of entities of any type using the Load Entity List API endpoint: /entities/:entityName. This API includes pagination, sorting, and fetch plans.

Request
GET http://localhost:8080/rest/entities/sample_Customer
Response: HTTP 200 - OK
[
  {
    "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"
  }
]
Every entity in the response has a _entityName attribute with the name of the entity, and an _instanceName attribute with the Instance Name of the entity.

It is also possible to further control the behavior of the API by using the following URL query parameters:

dynamicAttributes

whether dynamic attributes should be loaded for an entity (Boolean).

fetchPlan

name of an entity’s fetch plan (String).

limit

the number of entities to be returned by the API (int).

offset

the position of the first returned entity (int).

sort

an entity attribute that will be used for sorting (String).

  • +attribute or simply attribute for ascending order

  • -attribute for descending order.

Using Sorting

The Load Entities API supports the sorting of the result by entity attributes. You can use the sort URL parameter for controlling the order of entities.

When the sort parameter is not specified, the default sort order depends on the database implementation. Normally databases sort by the timestamp of record creation, but this behavior is not guaranteed and can vary in different situations.

Jmix has a special syntax to define the sort order. Ascending order is expressed through a + before the attribute name. This is optional though, as it is the default behavior sorting order. For descending order, you need to prefix the entity attribute with a - character.

The following example shows how you can sort Customers by their name attribute ascending.

Request
GET http://localhost:8080/rest
            /entities
            /sample_Customer
            ?sort=name
Response: HTTP 200 - OK
[
  {
    "id": "d83c9d66-cb23-075a-8d3c-d4035d338705",
    "name": "Klaudia Kleinert"
  },
  {
    "id": "8985ba1e-1cc8-eb5c-f9e0-738aee9d2ef1",
    "name": "Randall Bishop"
  }
]

You can also sort by multiple attributes. In this case, the sort order takes a comma-separated list of attributes to sort by.

Request
GET http://localhost:8080/rest
            /entities
            /sample_Order?sort=+date,-amount
Response: HTTP 200 - OK
[
  {
    "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 The result with the oldest date is returned first.
2 When the date attribute is the same, the amount is used to sort the results.

Using Pagination

The Entities API supports Pagination to respect the data processing limitation that might be present on the server or client-side. In case you want to load only a particular subset of the entities, you can provide the offset and limit URL parameters.

Pagination is active by default, even if it is not explicitly requested by the client. In case no limit value is present in the request, the Load APIs will return only the first 10,000 entities.

This default value is configurable globally via jmix.rest.default-max-fetch-size or on an entity-by-entity basis via jmix.rest.entityMaxFetchSize.

The following example demonstrates how to load the third Page containing two Customer entities (5. & 6. entity):

Load Customer Request with Pagination
GET http://localhost:8080/rest
            /entities
            /sample_Customer
            ?limit=2
            &offset=4
            &sort=createdDate
Response: HTTP 200 - OK
[
  {
    "id": "2d620164-1e80-0696-c3aa-45b7b5c81f2c",
    "name": "Maria Mitchell"
  },
  {
    "id": "3c7ec69d-9b85-c6e9-387b-42a5bccb79de",
    "name": "Anthony Knutson"
  }
]

Load Entities via Search Filter

You can specify filter criteria when loading entities using the Entity Search Endpoint: /entities/:entityName/search.

Both HTTP methods GET and POST are possible when interacting with the search endpoint. In both cases, the filter criterion has to be provided as part of the request.

The filter definition is a JSON structure that contains a set of conditions. A condition consists of the following attributes:

property

the entity attribute that is being filtered on (like amount on the Order entity).

In case the attribute is a reference to another entity, it can also be a property path like customer.name

operator

the filter operator. An operator describes how to filter for a particular attribute. There are multiple operators that can be used independently of the datatype:

  • Standard Operators: =, <>, notEmpty, isNull

  • List Operators: in, notIn

Additionally, some operators are only possible for particular datatypes:

Datatype Specific Operators

String, UUID

startsWith, endsWith, contains, doesNotContain

Integer, Long, Double, BigDecimal, Date, DateTime, Time, LocalDate, LocalDateTime, LocalTime, OffsetDateTime, OffsetTime

=, <>, >, >=, <, <=

value

the value to search for. Value is not required for the notEmpty and isNull operators.

Additionally, conditions can be combined via AND, OR group conditions to define a more complex filter criterion. The JSON structure of the filter definitions looks like this:

Filter Criterion JSON structure
{
  "conditions": [
    {
      "group": "OR",
      "conditions": [
        {
          "property": "stringField",
          "operator": "=",
          "value": "stringValue"
        },
        {
          "property": "intField",
          "operator": ">",
          "value": 100
        }
      ]
    },
    {
      "property": "booleanField",
      "operator": "=",
      "value": true
    }
  ]
}

This is a representation of the Filter criterion: ((stringField = stringValue) OR (intField > 100) AND (booleanField = true)).

When using the HTTP POST method, the filter is part of the request body.

Filter POST Request
POST http://localhost:8080/rest/entities/sample_Order/search

{
  "filter": {
    "conditions": [
      {
        "property": "customer.name",
        "operator": "=",
        "value": "Shelby Robinson"
      }
    ]
  }
}

When using the GET method, the JSON filter criterion needs to be transferred via the URL Query parameter filter.

Filter GET Request
GET http://localhost:8080/rest
            /entities
            /sample_Order
            /search
            ?filter={"conditions":[{"property":"customer.name","operator":"contains","value":"Shelby"}]}
URI Encoding

The HTTP URI standard only allows ASCII characters as part of the URI / URL. When using URL Query parameters for the filter definition, the JSON definition has to be URL encoded to match this requirement. This is also true for the value data that normally contains user input.

As there is also a practical limit of the URI length that can cause problems for big filter definitions, the POST method for filtering entities should be preferred as those restrictions do not apply.

Load Entities via JPQL

Another alternative to loading entities from the application is to use predefined JPQL queries. The Entity Query Endpoint /queries/:entityName/:queryName is responsible for providing this capability. Queries can contain a list of parameters, that need to be provided by the client. Additionally, the endpoint contains the same general parameters for pagination, fetch plans, etc.

When to use JPQL vs. Search Filter?

Jmix provides various ways to load entity data generically. Use pre-defined JPQL queries when the search filter is not advanced enough to express the filter criterion. Also in case, the parameter should be pre-defined and not be adjustable by the API client.

JPQL Query Configuration

To use the Entity Query endpoint you need to define the accessible queries. This happens via an XML configuration file, normally called rest-queries.xml. You need to create this new file in your Jmix application under src/main/resources. It lists all published queries with information about their parameters.

rest-queries.xml
<?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>

A query needs to have a unique name value as well as an entity reference. The combination of name and entity needs to be unique. Also, a fetchPlan needs to be referenced to indicate which entity attributes are returned.

In the <jpql> tag the actual query is configured. The parameters need to be listed within the params tag defining their name and Java type. In the query parameters, you can reference via their name prefixed with a colon like :customerName.

After the file has been created, and the queries have been defined, you need to register the rest-queries.xml configuration in the application.properties of your Jmix application:

application.properties
jmix.rest.queries-config = rest/sample/rest-queries.xml

You can invoke the Entity Query endpoint either by the GET or POST HTTP method. In the case of GET, the parameters are appended as URL query parameters.

Query API GET Request
GET http://localhost:8080/rest
        /queries
        /sample_Order
        /ordersByDate
        ?orderDate=2020-02-02
URI Encoding

The URL should only contain ASCII characters. This means the values of the parameters need to be URL encoded, as those values normally represent direct user input and therefore it cannot be ensured that non-ASCII characters are used.

In the case of using POST, the query parameters are transferred in the JSON body containing each parameter as a key.

Query API POST Request
POST http://localhost:8080/rest/queries/sample_Order/ordersByCustomerName

{
  "customerName": "Shelby Robinson"
}

Collection Parameters

It is also possible to define a parameter as a collection type. In this case, the query definition should contain an [] indicator after the Java type.

rest-queries.xml
<?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 The ids parameter is marked as collection of UUID type.

When this parameter is used in a query, the corresponding IDs have to be provided as a JSON array.

Query API Collection Parameters Request
POST http://localhost:8080/rest/queries/sample_Order/ordersByIds

{
  "ids": [
    "41aae331-b46b-85ee-b0bc-2de8cbf1ab86",
    "21021f78-edac-224b-e6f8-6e71e02a0f0d"
  ]
}

Return Empty Values in JSON

By default, Jmix will remove empty values (null) from the JSON response, so that the attribute keys are not present in the JSON document.

You can control this behavior by using the URL query parameter returnNulls and set its value to true. With that, Jmix will always add the attribute keys to the response, independent if the value is empty or not.

In the following example, a Customer is loaded by its ID and also requesting to contain all empty values:

Load Customer with empty values
GET http://localhost:8080/rest
            /entities
            /sample_Customer
            /1eab4973-25f9-70d9-5356-6990dd8f79e2
            ?returnNulls=true
Response: 200 - OK
{
  "_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 The response contains the key type although it is empty
The Parameter returnNulls is present in all Entity Load APIs: Load by ID, Load List, Search and Load by Query.