How data context works

IntelliJ IDEA 2024.3
Jmix version: 2.5.0
Jmix Studio Plugin Version: 2.5.0-243
test.zip (1.0 MB)
Hello, I encountered the following problem:
I have two entities, A and B:

public class A {
        ....
        @Composition
        @OneToMany(mappedBy = "a")
        private List<B> b;
        ....
}

public class B {
        ....
        @OnDeleteInverse(DeletePolicy.CASCADE)
        @JoinColumn(name = "A_ID", nullable = false)
        @ManyToOne(fetch = FetchType.LAZY, optional = false)
        private A a;
        .....
}

I use a custom action to create an entity B from ADetailView:

    @Subscribe("bDataGrid.createAction")
    public void onBDataGridCreateAction(final ActionPerformedEvent event) {
        dialogWindows.detail(this, B.class)
                .withViewClass(BDetailView.class)
                .withAfterCloseListener(closeEvent -> {
                    if (closeEvent.closedWith(StandardOutcome.SAVE)) {
                        B b = closeEvent.getView().getEditedEntity();
                        b.setA(getEditedEntity());
                        bDc.getMutableItems().add(b);
                    }
                })
                .withParentDataContext(dataContext)
                .newEntity()
                .build()
                .open();
    }

When I try to save A with B, I get the following exception:
Caused by: org.postgresql.util.PSQLException: ERROR: null value in column “a_id” of relation “b” violates not-null constraint
It happens because Jmix sets the b field in A to null.
However, if I slightly modify the code, everything works fine:

    @Subscribe("bDataGrid.createAction")
    public void onBDataGridCreateAction(final ActionPerformedEvent event) {
        dialogWindows.detail(this, B.class)
                .withViewClass(BDetailView.class)
                .withAfterCloseListener(closeEvent -> {
                    if (closeEvent.closedWith(StandardOutcome.SAVE)) {
                        B b = closeEvent.getView().getEditedEntity();
                        bDc.getMutableItems().add(b);
                    }
                })
                .withInitializer(it -> it.setA(getEditedEntity()))
                .withParentDataContext(dataContext)
                .newEntity()
                .build()
                .open();
    }

I would like to know the correct way to handle a situation where I have multiple entities, nested views, and want all saves to be performed when saving the root screen.

Hi,

To work with composition within single data context you basically don’t need custom action.

Once you declare standard create/edit actions with dialog mode (you can’t handle composition with navigation mode) the framework do all necessary operations (using single data context, update datagrid).
e.g.

<dataGrid id="itemsDataGrid" dataContainer="itemsDc" width="100%" minHeight="20em">
    <actions>
        <action id="createAction" type="list_create">
            <properties>
                <property name="openMode" value="DIALOG"/>
            </properties>
        </action>
        <action id="editAction" type="list_edit">
            <properties>
                <property name="openMode" value="DIALOG"/>
            </properties>
        </action>
        <action id="removeAction" type="list_remove"/>
    </actions>
    <columns>
        <column property="name"/>
    </columns>
</dataGrid>

If for some reason you actually need to implement your own action, your second variant looks ok as minimal working example. But at the same time it doesn’t look necessary to be implemented as custom action. Default behaviour should fit.

Also take a look at Data Modeling: Composition :: Jmix Documentation - this guide is created specifically for compositions. There are some useful cases.
And you can find some examples in https://demo.jmix.io/ui-samples

Regards,
Ivan