Обновление сущностей

Entities API позволяет изменять уже существующие сущности через конечную точку /entities/:entityName/:entityId методом HTTP PUT.

Обновить сущность

При успешном обновлении сущности возвращается код состояния HTTP 200 - OK. По умолчанию возвращается представление метаданных сущности в формате JSON, в основном содержащее только что созданный атрибут id для дальнейшего использования.

Пример обновления сущности:

Create Customer Request
PUT http://localhost:8080/rest
            /entities
            /sample_Customer
            /13f01f59-8e5f-4fd9-802b-66501d49ac99

{
  name: "Updated Name"
}
Response: 200 - OK
{
  "_entityName": "sample_Customer",
  "_instanceName": "Updated Name",
  "id": "13f01f59-8e5f-4fd9-802b-66501d49ac99"
}

Update Entity API работает аналогично Create Entities API в отношении валидации и ссылок на сущности. Основное различие заключается в том, как обрабатываются ассоциации и композиции 1:N. Эта операция также позволяет изменять сущности частично, имея в запросе JSON только эти атрибуты.

Атрибуты ассоциации

Если вы хотите обновить атрибут ассоциации 1:1 или N:1, достаточно отправить новый id ссылаемой сущности. Для ассоциаций 1:N или M:N обновленный список ссылок на сущности изменяет то, с чем связана сущность.

Конечная точка Update Entity заменит существующую коллекцию ассоциаций на 1:N или M:N ассоциацию с тем, что является частью запроса. Каждая ссылка на сущность, которая не является частью запроса, будет удалена. Но сущность, на которую была сделана ссылка, не будет удалена из базы данных.

В этом примере показано, как обновить ассоциацию M:N между Product и ProductTag. Предполагается, что сохраненный Product до изменения выглядит так:

Existing Product
{
  "id": "e1d586b4-aefb-2ee7-3b91-b07357b178ea",
  "price": 99.95,
  "name": "Outback Power Remote Power System",
  "version": 1,
  "tags": [
    {
      "id": "333f3a20-c47b-4bc9-ba34-a72d2d815695",
      "name": "shiny"
    },
    {
      "id": "c4c028f0-fec1-7512-83cd-c17537d1f502",
      "name": "great"
    }
  ]
}

В этом примере мы хотели бы изменить теги Product следующими способами:

  • great должен быть удален из тегов.

  • amazing необходимо добавить в теги.

  • Тег shiny должен быть сохранен.

Update Tag Associations Request
PUT http://localhost:8080/rest
            /entities
            /sample_Product
            /e1d586b4-aefb-2ee7-3b91-b07357b178ea
            ?responseFetchPlan=product-with-tags

{
  "name": "123",
  "price": 99.95,
  "tags": [
    {
      "id": "333f3a20-c47b-4bc9-ba34-a72d2d815695" (1)
    },
    {
      "id": "d6ab132e-a0bd-a624-c6ad-cc544e83c584" (2)
    }
  ]
}
1 Идентификатор тега продукта shiny является частью запроса на сохранение ассоциации.
2 Идентификатор тега продукта amazing вновь добавляется в ассоциацию.
Поскольку идентификатор тега продукта great (c4c028f0-fec1-7512-83cd-c17537d1f502) больше не является частью запроса, он будет удален из ассоциации.
Response: 200 - OK
{
  "id": "e1d586b4-aefb-2ee7-3b91-b07357b178ea",
  "createdBy": "admin",
  "price": 99.95,
  "name": "Outback Power Remote Power System",
  "version": 2,
  "tags": [
    {
      "id": "333f3a20-c47b-4bc9-ba34-a72d2d815695",
      "name": "shiny" (1)
    },
    {
      "id": "d6ab132e-a0bd-a624-c6ad-cc544e83c584",
      "name": "amazing" (2)
    }
  ]
}
1 Ссылка shiny все еще присутствует, так как она была частью запроса.
2 Ссылка amazing была добавлена, тогда как тег great больше не является частью ассоциации.
Удалить ссылку на сущность *:1

Чтобы удалить ссылку ассоциации N:1 или 1:1, нужно отправить null в качестве значения. Отсутствие атрибута в запросе не приведет к удалению ссылки из-за функции частичного обновления. В этом случае атрибут будет просто проигнорирован и не изменен как часть запроса.

Атрибуты композиции

Если вы хотите обновить атрибут композиции, можно напрямую обновить содержимое дочерней сущности как часть запроса на обновление для родительской сущности. Это возможно как для композиций 1:1, так и 1:N.

Операция Update Entity заменит существующую коллекцию композиций тем, что является частью запроса. Каждая ссылка на сущность, которая не является частью запроса, будет удалена. Кроме того, сущность, на которую была сделана ссылка, будет также удалена из приложения.

В этом примере показано, как обновить композицию 1:N между Order и OrderLine. Предполагается, что сохраненный Order до изменения выглядит так:

Existing Order
{
  "id": "288a5d75-f06f-d150-9b70-efee1272b96c",
  "date": "2021-03-01",
  "amount": 130.08,
  "lines": [
    {
      "id": "a1cd778b-fe49-4c74-05a0-6fb207dc11bd",  (1)
      "product": {
        "id": "1860904a-5444-9c3e-9dc1-1d7a26d9ac19",
        "name": "Solar-One HUP Flooded Battery 48V"
      },
      "quantity": 2.0,
      "version": 1
    },
    {
      "id": "55b925e5-9f3a-a725-9eb3-1240f9c1fe95",  (2)
      "product": {
        "id": "1ed85c7a-89f1-c339-a738-16307ed6003a",
        "name": "Cotek Battery Charger"
      },
      "quantity": 1.0,
      "version": 1
    }
  ],
  "version": 1,
  "customer": {
    "id": "f88597ff-009d-1cf2-4a90-a4fb5b08d835",
    "name": "Randall Bishop"
  }
}
1 Первая строка заказа ссылается на продукт Solar-One HUP Flooded Battery 48V.
2 Вторая строка заказа ссылается на продукт Cotek Battery Charger.

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

  • Строка заказа quantity с товаром Solar-One HUP Flooded Battery 48V должна быть изменена на 3.0.

  • Строка заказа с товаром Cotek Battery Charger должна быть удалена.

  • Должна быть добавлена новая строка заказа с продуктом Outback Power Remote Power System.

Update Composition Request
PUT http://localhost:8080/rest
            /entities
            /sample_Order
            /288a5d75-f06f-d150-9b70-efee1272b96c
            ?responseFetchPlan=product-with-tags

{
  "customer": {
    "id": "f88597ff-009d-1cf2-4a90-a4fb5b08d835"
  },
  "date": "2021-03-01",
  "amount": 249.99,
  "lines": [
    {
      "id": "a1cd778b-fe49-4c74-05a0-6fb207dc11bd", (1)
      "product": {
        "id": "1860904a-5444-9c3e-9dc1-1d7a26d9ac19",
        "name": "Solar-One HUP Flooded Battery 48V"
      },
      "quantity": 3.0 (2)
    },
    { (3)
      "product": {
        "id": "f6884077-19c4-546f-33d4-a788399337f7",
        "name": "Outback Power Remote Power System"
      },
      "quantity": 1.0
    }
  ]
}
1 Идентификатор существующей строки заказа добавляется для ее обновления.
2 Значение quantity для продукта 3.0Solar-One HUP Flooded Battery 48V установлено в 3.0.
3 Добавлена новая строка заказа с продуктом Outback Power Remote Power System
При обновлении дочерней сущности, такой как строка заказа в приведенном выше примере, необходимо добавить идентификатор существующей строки заказа, чтобы Jmix распознал его как обновление. В противном случае он будет рассматривать дочернюю сущность как новую.

Ответ на этот запрос на обновление содержит желаемые изменения:

Response: 200 - OK
{
  "id": "288a5d75-f06f-d150-9b70-efee1272b96c",
  "date": "2021-03-01",
  "amount": 249.99,
  "lines": [
    {
      "id": "d0fdfaa8-7d65-5e25-49c2-d34fc41c0e55",
      "product": {
        "id": "1860904a-5444-9c3e-9dc1-1d7a26d9ac19",
        "name": "Solar-One HUP Flooded Battery 48V"
      },
      "quantity": 3.0, (1)
      "version": 2 (2)
    },
    {
      "id": "96722466-5164-a48c-b7f6-8d4c1bd605dd",
      "product": {
        "id": "f6884077-19c4-546f-33d4-a788399337f7",
        "name": "Outback Power Remote Power System" (3)
      },
      "quantity": 1.0
    }
  ],
  "version": 2,
  "customer": {
    "id": "f88597ff-009d-1cf2-4a90-a4fb5b08d835",
    "name": "Randall Bishop 3"
  }
}
1 Значение quantity для Solar-One HUP Flooded Battery 48V было обновлено.
2 Атрибут version был увеличен, чтобы обозначить обновление.
3 В заказ добавлена новая строка для Outback Power Remote Power System.

Строки заказа были успешно обновлены.

Когда дочерняя сущность не должна обновляться, но по-прежнему должна оставаться в композиции, id существующей строки заказа, тем не менее, должен быть частью запроса. Тогда Jmix распознает его как часть композиции и не станет удалять. Отсутствие этого id в запросе приведет к тому, что Jmix удалит сущность (поскольку именно так обрабатывается @Composition), что может неверно посчитаться случайным поведением.

В приведенном выше примере несмотря на то, что строку заказа для Cotek Battery Charger (55b925e5-9f3a-a725-9eb3-1240f9c1fe9) не было нужно изменять, она все равно должна быть указана в обновлении с ее идентификатором:

Update Composition Request containing non-changing child entities
{
  "lines": [
    {
      "id": "55b925e5-9f3a-a725-9eb3-1240f9c1fe9" (1)
    },
    {
      "id": "a1cd778b-fe49-4c74-05a0-6fb207dc11bd",
      "product": {
        "id": "1860904a-5444-9c3e-9dc1-1d7a26d9ac19",
        "name": "Solar-One HUP Flooded Battery 48V"
      },
      "quantity": 3.0
    },
    {
      "product": {
        "id": "f6884077-19c4-546f-33d4-a788399337f7",
        "name": "Outback Power Remote Power System"
      },
      "quantity": 1.0
    }
  ]
}
1 На дочерние сущности, которые не были изменены (например Cotek Battery Charger), но все еще должны быть частью композиции, есть ссылки через их id.

Ограничения безопасности для ассоциаций/композиций

Как мы узнали выше, операция Update Entity заменит существующую коллекцию ассоциаций/композиций тем, что является частью запроса. Для каждой ссылки на сущность, которая не является частью запроса, ссылка будет удалена. Кроме того, сущность, на которую ссылались ранее, будет также удалена из приложения.

Имея это в виду, давайте рассмотрим пример сущности с активным ограничением безопасности Jmix на уровне строк:

Предположим, что мы загружаем экземпляр Order вместе с вложенной коллекцией экземпляров OrderLine.

Существуют ограничения безопасности, которые отфильтровывают некоторые экземпляры OrderLine, поэтому мы их не загружаем и не знаем, что они существуют. Например, line5 не загружается клиентом, но существует в базе данных. Если мы обновим Order и удалим line2 из строк заказа, возможны два результата:

  • Если ограничения не изменялись с момента загрузки сущностей, фреймворк восстановит отфильтрованный экземпляр line5 в коллекции и удалит только line2, что является корректным поведением.

  • Если ограничения были изменены таким образом, что теперь line5 нам доступен, фреймворк не сможет корректно восстановить информацию об отфильтрованных элементах коллекции. В результате и line2, и line5 будут удалены.

Чтобы исключить возможность потери данных, необходимо отправить специальный системный атрибут в JSON, представляющий наши сущности. Это атрибут __securityToken, и он включается в результирующий JSON автоматически, если для свойства приложения jmix.core.entitySerializationTokenRequired задано значение true.

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

Request with Security Token
{
  "id": "fa430b56-ceb2-150f-6a85-12c691908bd1",
  "lines": [
    {
      "id": "82e6e6d2-be97-c81c-c58d-5e2760ae095a",
      "description": "Item 1"
    },
    {
      "id": "988a8cb5-d61a-e493-c401-f717dd9a2d66",
      "description": "Item 2"
    }
  ],
  "__securityToken": "0NXc6bQh+vZuXE4Fsk4mJX4QnhS3lOBfxzUniltchpxPfi1rZ5htEmekfV60sbEuWUykbDoY+rCxdhzORaYQNQ==" (1)
}
1 Токен безопасности — это значение, которое Load Entities API получил ранее.

Атрибут __securityToken содержит закодированные идентификаторы отфильтрованных экземпляров, поэтому фреймворк всегда может восстановить необходимую информацию вне зависимости от изменения ограничений.

Частичные обновления

Можно отправлять только те атрибуты, которые должны быть изменены. При этом все остальные атрибуты сущности останутся нетронутыми.

В приведенном ниже примере сущности Order мы отправим только измененную дату заказа. Хотя сущность Order содержит и другие атрибуты, такие как customer, amount, lines.

Partial Order Update Request
PUT http://localhost:8080
         /entities
         /sample_Order
         /5a8adc2f-f4ef-17a9-9f97-1e715b3ade3d

{
  "date": "2020-12-06"
}
Response: 200 - OK
{
  "id": "5a8adc2f-f4ef-17a9-9f97-1e715b3ade3d",
  "date": "2020-12-06", (1)
  "amount": 130.08, (2)
  "version": 2 (3)
}
1 Атрибут date был изменен с новой датой заказа.
2 Остальные атрибуты сущности остаются нетронутыми.
3 Атрибут version сущности Order был увеличен, чтобы обозначить обновление.

Массовое обновление

Update Entity API позволяет обновлять несколько сущностей в одном запросе. Для этого тело запроса JSON должно содержать массив объектов JSON, представляющих каждую сущность.

Bulk Update Request
PUT http://localhost:8080/rest
            /entities
            /sample_Customer

[
  {
    "name": "Randall Bishop 2"
  },
  {
    "name": "Sarah Doogle 2"
  }
]
Response: 200 - OK
[
  {
    "_entityName": "sample_Customer",
    "_instanceName": "Randall Bishop 2",
    "id": "833a610b-bc2c-2f44-c67a-2cf8b25f3291"
  },
  {
    "_entityName": "sample_Customer",
    "_instanceName": "Sarah Doogle 2",
    "id": "c8ab5ae2-7f8f-bc68-fb58-6cfcf7b1d235"
  }
]

В случае нарушения валидации, сущности не будут созданы, и будет возвращено соответствующее сообщение об ошибке. Подробнее см. в разделе Валидация сущности.