4.5.3.5. DsContext
Все созданные декларативно источники данных регистрируются в объекте DsContext
экрана. Ссылку на DsContext
можно получить методом getDsContext()
контроллера экрана, либо инжекцией в поле класса.
DsContext
решает следующие задачи:
-
Позволяет организовать зависимости между источниками данных, когда при навигации по одному источнику (т.е. при изменении "текущего" экземпляра методом
setItem()
) обновляется связанный источник. Такие зависимости дают возможность в экранах легко организовывать master-detail связи между визуальными компонентами.Зависимости между источниками организуются с помощью параметров запросов с префиксом
ds$
. -
Позволяет собрать все измененные экземпляры сущностей и отправить их на Middleware в одном вызове
DataManager.commit()
, т.е. сохранить в базе данных в одной транзакции.В качестве примера предположим, что некоторый экран позволяет редактировать экземпляр сущности
Order
и коллекцию принадлежащих ему экземпляровOrderLine
. ЭкземплярOrder
находится вDatasource
, коллекцияOrderLine
- во вложенномCollectionDatasource
, созданном по атрибутуOrder.lines
. Допустим, пользователь изменил какой-то атрибутOrder
и создал новый экземплярOrderLine
. Тогда при коммите экрана в DataManager будут одновременно отправлены два экземпляра - измененныйOrder
и новыйOrderLine
. Далее, они вместе попадут в один персистентный контекст и при коммите транзакции сохранятся в БД. Разумеется, экземплярOrderLine
содержится также в коллекцииOrder.lines
, но если не передавать его в персистентный контекст независимо, то потребуется установка каскадности сохранения междуOrder
иOrderLines
на уровне ORM. Жесткие отношения каскадности на уровне ORM иногда вызывают нежелательные последствия в неожиданных местах, поэтому лучше их избегать, что и обеспечивает описываемый механизмDsContext
.В результате коммита
DsContext
получает от Middleware набор сохраненных экземпляров (в случае оптимистической блокировки у них, как минимум, увеличено значение атрибутаversion
), и устанавливает эти экземпляры в источниках данных взамен устаревших. Это позволяет сразу после коммита работать со свежими экземплярами без необходимости лишнего обновления источников данных, связанного с запросами к Middleware и базе данных. -
Объявляет два слушателя:
BeforeCommitListener
иAfterCommitListener
, позволяющие получать оповещения перед коммитом измененных экземпляров и после него. Перед коммитом можно дополнить коллекцию отправляемых в DataManager на Middleware экземпляров, тем самым обеспечив сохранение в той же транзакции произвольных сущностей. После коммита можно получить коллекцию вернувшихся изDataManager
сохраненных экземпляров.Данный механизм необходим, если некоторые сущности, с которыми работает экран, находятся не под управлением источников данных, а создаются и изменяются непосредственно в коде контроллера. Например, визуальный компонент
FileUploadField
после загрузки файла создает новый экземпляр сущностиFileDescriptor
, который можно сохранить вместе с другими сущностями экрана именно таким способом - добавив вCommitContext
в слушателеBeforeCommitListener
.В следующем примере новый экземпляр
Customer
будет отправлен на Middleware и сохранен в БД вместе с остальными измененными сущностями экрана при его коммите:protected Customer customer; protected void createNewCustomer() { customer = metadata.create(Customer.class); customer.setName("John Doe"); } @Override public void init(Map<String, Object> params) { getDsContext().addBeforeCommitListener(context -> { if (customer != null) context.getCommitInstances().add(customer); } }