Clone an entity record with two levels of one-to-many composition nesting from the entity's browser screen

I’m having troubles trying to clone an entity record that contains two levels of nested compositions (one-to-many relationship) from the main entity’s browser screen.

Taking for example the sample-model application provided for illustrating the deep composition nesting at GitHub - cuba-platform/sample-model: DEPRECATED. See examples for data modelling guides at https://www.cuba-platform.com/guides.

I’ve added a “Clone” button on the main browse screen:
image

… and wrote the following code to handle the button click event:

public void onBtnCloneClick() {
    Airport selectedAirport = dataManager.load(Airport.class).id(airportsTable.getSingleSelected().getId()).view("airport-terminals-meetingPoints").one();
    Airport airport = metadata.getTools().deepCopy(selectedAirport);
    // Generate new IDs for the new object and nested compositions: 
    airport.setId(UUID.randomUUID());
    for (Terminal terminal : airport.getTerminals()) {
        terminal.setId(UUID.randomUUID());
        for (MeetingPoint meetingPoint : terminal.getMeetingPoints()) meetingPoint.setId(UUID.randomUUID());
    }
/* // case 1:
    airportsDs.addItem(airport);
    airportsDs.commit();*/
/* // case 2:
    openEditor(airport, WindowManager.OpenType.THIS_TAB).addCloseWithCommitListener(() -> {
        airportsDs.refresh();
        airportsTable.setSelected(airport);
        airportsTable.requestFocus();
    });*/
}

If I uncomment case 1 in the above code, I sometimes get the following error:
IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST: com.company.sample.entity.airports.Terminal-22f5afa5-5fab-4126-a498-95a0e02a2e0e [new].
or (occasionally) the error below, thrown when using case 2.

If I try to use case 2, I get this:
IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST: com.company.sample.entity.airports.MeetingPoint-0a2835aa-1498-48e8-9b64-ed6625455737 [new].

If I add “cascade = CascadeType.ALL” to the @OneToMany annotation for the Terminal entity, the commit succeeds in both cases with no errors and the related entities are properly populated.

I’ve stumbled upon a topic on the discussion forums which advises not to use the “cascade = CascadeType.ALL” annotation…

Could you please elaborate on what’s the correct way of copying/cloning a record for an entity of that type?

Regards,
Georgi

Hi. I don’t know about the best practices of whether or not to use the CascadeType.ALL but I do know that if you want to avoid using it, you need to add the nested items to the commitcontext as well:

@Inject
private DataManager dm;

public void onBtnCloneClick() {
    Airport selectedAirport = dataManager.load(Airport.class).id(airportsTable.getSingleSelected().getId()).view("airport-terminals-meetingPoints").one();
    Airport airport = metadata.getTools().deepCopy(selectedAirport);
    CommitContext cc = new CommitContext();
    // Generate new IDs for the new object and nested compositions: 
    airport.setId(UUID.randomUUID());
    for (Terminal terminal : airport.getTerminals()) {
        terminal.setId(UUID.randomUUID());
        for (MeetingPoint meetingPoint : terminal.getMeetingPoints()) {
             meetingPoint.setId(UUID.randomUUID());
             cc.addInstanceToCommit(meetingPoint);
        }
        cc.addInstanceToCommit(terminal);
    }
    cc.addInstanceToCommit(airport);
    dm.commit(cc);
}

Didn’t test this scenario but I believe it should work.

-b

Also, setting random UUID might be errornous as objects relate to each other using such ID’s and may thus become invalid. It’s not a scenario I would recommend in general.