3.5.3.3. DataContext
DataContext
is an interface for tracking changes in entities loaded to the client tier. Tracked entities are marked as "dirty" on any modification of their attributes, and DataContext
saves dirty entities to the middle tier when its commit()
method is invoked.
Within DataContext
, an entity with the given identifier is represented by a single object instance, no matter where and how many times it is used in object graphs.
In order to be tracked, an entity must be put into DataContext
using its merge()
method. If the context does not contain the entity with the same id, the context creates a new instance, copies the state of the passed instance to the new one and returns it. If the context already contains an instance with the same id, it copies the state of the passed instance to the existing one and returns it. This mechanism allows the context to always have only one instance of an entity with a particular identifier.
When you merge an entity, the whole object graph with the root in this entity will be merged. I.e. all referenced entities (including collections) will become tracked.
The main rule of using the |
Example of merging an entity into DataContext
:
@Inject
private DataContext dataContext;
private void loadCustomer(Id<Customer, UUID> customerId) {
Customer customer = dataManager.load(customerId).one();
Customer trackedCustomer = dataContext.merge(customer);
customersDc.getMutableItems().add(trackedCustomer);
}
A single instance of DataContext
exists for a given screen and all its nested fragments. It is created if the <data>
element exists in the screen XML descriptor.
The <data>
element can have readOnly="true"
attribute, in that case a special "no-op" implementation is used which actually doesn’t track entities and hence doesn’t affect performance. By default, entity browsers scaffolded by Studio have the read-only data context, so if you need to track changes and commit dirty entities in a browser, remove the readOnly="true"
XML attribute.
- Obtaining DataContext
-
DataContext
of a screen can be obtained in its controller using injection:@Inject private DataContext dataContext;
If you have a reference to a screen, you can get its
DataContext
using theUiControllerUtils
class:DataContext dataContext = UiControllerUtils.getScreenData(screenOrFrame).getDataContext();
A UI component can obtain
DataContext
of the current screen as follows:DataContext dataContext = UiControllerUtils.getScreenData(getFrame().getFrameOwner()).getDataContext();
- Parent DataContext
-
DataContext
instances can form parent-child relationships. If aDataContext
instance has parent context, it commits changed entities to the parent instead of saving them to the middle tier. This feature enables editing compositions, when detail entities are saved only together with the master entity. If an entity attribute is annotated with @Composition, the framework automatically sets parent context in the attribute editor screen, so the changed attribute entity will be saved to the data context of the master entity.You can easily provide the same behavior for any entities and screens.
If you open an edit screen which should commit data to the current screen’s data context, use
withParentDataContext()
method of the builder:@Inject private ScreenBuilders screenBuilders; @Inject private DataContext dataContext; private void editFooWithCurrentDataContextAsParent() { FooEdit fooEdit = screenBuilders.editor(Foo.class, this) .withScreenClass(FooEdit.class) .withParentDataContext(dataContext) .build(); fooEdit.show(); }
If you open a simple screen using the
Screens
bean, provide a setter method accepting the parent data context:public class FooScreen extends Screen { @Inject private DataContext dataContext; public void setParentDataContext(DataContext parentDataContext) { dataContext.setParent(parentDataContext); } }
And use it after creating the screen:
@Inject private Screens screens; @Inject private DataContext dataContext; private void openFooScreenWithCurrentDataContextAsParent() { FooScreen fooScreen = screens.create(FooScreen.class); fooScreen.setParentDataContext(dataContext); fooScreen.show(); }
Make sure the parent data context is not defined with
readOnly="true"
attribute. Otherwise you will get an exception when try to use it as a parent for another context.