All datasources that are created declaratively are registered in the DsContext object of a screen. A reference to DsContext can be obtained using the getDsContext() method of a screen controller or with an injection into a class field.
DsContext solves the following tasks:
-
Organizes dependencies between datasources when with a navigation to one source (i.e. when changing a "current" instance with the
setItem()method) a related source is updated. Using these dependencies it's quite easy to organize master-detail connections among visual components on screens.Dependencies between sources are organized using query parameters with the
ds$prefix. -
Collects all changed entity instances and sends them to Middleware in a single invocation of
DataManager.commit(), i.e. to save them into a data base using a single transaction.As an example, let's assume that some screen allows a user to edit an instance of the
Orderentity and a collection ofOrderLineinstances belonging to it. TheOrderinstance is located inDatasource; theOrderLinecollection – in nestedCollectionDatasource, which is created using theOrder.linesattribute. If user changes some attribute ofOrderand creates a new instance,OrderLine. Then, when a screen is committed to DataManager, two instances – changedOrderand newOrderLine– will be sent simultaneously. After that, they will together get into one persistent context and will be saved into the DB with transaction commit. TheOrderLineinstance is also contained in theOrder.linescollection, but if it's not passed into persistent context independently, the cascade saving betweenOrderandOrderLinesat the ORM level should be set. Tight cascade relations at the ORM level sometimes cause unwanted consequences in unexpected places, so it will be better to avoid them, as described in theDsContextmechanism.As a result of committing transaction,
DsContextreceives a set of saved instances from Middleware (in case of optimistic blocking they at least have an increased value of theversionattribute), and sets these instances in datasources instead of outdated ones. This allows you to work with latest instances immediately after committing without an extra data source refresh that is related to queries to Middleware and the database. -
Declares a listener,
DsContext.CommitListener, which allows to receive notifications before and after committing modified instances. Before the commit it's possible to supplement a collection of instances sent to DataManager at Middleware which will lead to saving arbitrary entities in the same transaction. A collection of saved instances that are returned fromDataManagercan be obtained after commit.This mechanism is required if some entities, with which a screen works, are not under control of datasources, but are created and changed directly in the controller code. For example, a visual component,
FileUploadField, after uploading a file, creates a new entity instance,FileDescriptor, which can be saved together with other screen entities by adding toCommitContextin theDsContext.CommitListener.beforeCommit()method..DsContext.CommitListenerhas theDsContext.CommitListenerAdapteradapter, which is useful when it's needed to define only one method.In the following example, a new instance,
Customerwill be sent to Middleware and saved to the DB together with other modified screen entities when it is committed:protected Customer customer; protected void createNewCustomer() { customer = new Customer(); customer.setName("John Doe"); } public void init(Map<String, Object> params) { getDsContext().addListener(new DsContext.CommitListenerAdapter() { @Override public void beforeCommit(CommitContext context) { if (customer != null){ context.getCommitInstances().add(customer); } } }); }

