4.5.3.4. DsContext

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:

  1. 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.

  2. 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 Order entity and a collection of OrderLine instances belonging to it. The Order instance is located in Datasource; the OrderLine collection – in nested CollectionDatasource, which is created using the Order.lines attribute. If user changes some attribute of Order and creates a new instance, OrderLine. Then, when a screen is committed to DataManager, two instances – changed Order and new OrderLine – will be sent simultaneously. After that, they will together get into one persistent context and will be saved into the DB with transaction commit. The OrderLine instance is also contained in the Order.lines collection, but if it's not passed into persistent context independently, the cascade saving between Order and OrderLines at 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 the DsContext mechanism.

    As a result of committing transaction, DsContext receives a set of saved instances from Middleware (in case of optimistic blocking they at least have an increased value of the version attribute), 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.

  3. 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 from DataManager can 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 to CommitContext in the DsContext.CommitListener.beforeCommit() method..

    DsContext.CommitListener has the DsContext.CommitListenerAdapter adapter, which is useful when it's needed to define only one method.

    In the following example, a new instance, Customer will 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);
                }
            }
        });
    }