Trouble on attaching multiple files to an entity

Cuba 7.2.4

Hello everyone, i am having trouble trying to modeling and manipulating the following scenario:

ColetoInventario entity have many ArquivoColeta entities, and one ArquivoColeta have a FileDescriptor association.

On the ColetaInventario edit screen, was add an option to upload multiple files, and then show there on the grid bellow. But, when saving the entity, the following error is displayed:

java.lang.IllegalStateException: An attempt to save an entity with reference to some not persisted entity.
All newly created entities must be saved in the same transaction. Put all these objects to the CommitContext before commit.

The intention is to generate the child entities along with the main record, but I am not getting success in this process.

This is the code of the entity edit screen controller I tried using:

@Subscribe
public void onInit(InitEvent event) {

    multiUploadField.addQueueUploadCompleteListener(queueUploadCompleteEvent -> {

        List<ArquivoColeta> listaArq = getEditedEntity().getArquivos();
        if (listaArq == null)
            listaArq = new ArrayList<ArquivoColeta>();

        for (Map.Entry<UUID, String> entry : multiUploadField.getUploadsMap().entrySet()) {
            UUID fileId = entry.getKey();
            String fileName = entry.getValue();
            FileDescriptor fd = fileUploadingAPI.getFileDescriptor(fileId, fileName);
            try {
                fileUploadingAPI.putFileIntoStorage(fileId, fd);

            } catch (FileStorageException e) {
                throw new RuntimeException("Error saving file to FileStorage", e);
            }
				
				//dataManager.commit(fd);

            ArquivoColeta coletaArquivos = metadata.create(ArquivoColeta.class);
            coletaArquivos.setFile(fd);
            coletaArquivos.setColetaInventario(getEditedEntity());
            coletaArquivos.setSituacao(SituacaoArquivo.PENDENTE);
            listaArq.add(coletaArquivos);

        }

        arquivosDc.setItems(listaArq);
        notifications.create()
                .withCaption("Arquivos carregados: " + multiUploadField.getUploadsMap().values())
                .show();
        multiUploadField.clearUploads();
    });

    multiUploadField.addFileUploadErrorListener(queueFileUploadErrorEvent -> {
        notifications.create()
                .withCaption("Erro ao Carregar os arquivos")
                .show();
    });
}

attached an example project
testeupfile.zip (82.3 KB)

Hi,
I haven’t checked sample project, but from the code you posted it is seen that you don’t put new ArquivoColeta to the data context.
In order for them to be saved on screen commit - they must be in the data context.

To do that, you should:

  • Inject DataContext to the screen controller
  • When creating new ArquivoColeta entities, use dataContext.create() instead of metadata.create().

See also similar topic: Loading Composition data programatically in V7 - CUBA.Platform

1 Like

change made and working

Thank you

the proposed changes have been made and the routine works.

but we identified a new situation after the routine was released, only the first addition of files is listed in the grid.
after having items in the grid, if the user adds more files, they are not displayed.
but when recording, and accessing the same record, it lists all the files inserted, in both steps.

apparently, it is as if the grid does not update with the data in memory.

something that can be done to list, before it is actually recorded in the database?

@Subscribe
public void onInit(InitEvent event) {

    multiUploadField.addQueueUploadCompleteListener(queueUploadCompleteEvent -> {

        List<ColetaArquivos> listaArq = getEditedEntity().getColetaArquivo();

        if (listaArq == null) {
            listaArq = new ArrayList<ColetaArquivos>();
        }

        for (Map.Entry<UUID, String> entry : multiUploadField.getUploadsMap().entrySet()) {
            UUID fileId = entry.getKey();
            String fileName = entry.getValue();
            FileDescriptor fd = fileUploadingAPI.getFileDescriptor(fileId, fileName);
            try {
                fileUploadingAPI.putFileIntoStorage(fileId, fd);

            } catch (FileStorageException e) {
                throw new RuntimeException("Error saving file to FileStorage", e);
            }
            dataManager.commit(fd);

            ColetaArquivos coletaArquivos = dataContext.create(ColetaArquivos.class);
            coletaArquivos.setArquivo(fd);
            coletaArquivos.setColetaInventario(getEditedEntity());
            coletaArquivos.setSituacao(SituacaoArquivo.PENDENTE);
            listaArq.add(coletaArquivos);
        }

        coletaInventarioDc.getItem().setColetaArquivo(listaArq);
        notifications.create()
                .withCaption("Arquivos carregados: " + multiUploadField.getUploadsMap().values())
                .show();
        multiUploadField.clearUploads();
    });

    multiUploadField.addFileUploadErrorListener(queueFileUploadErrorEvent -> {
        notifications.create()
                .withCaption("Erro ao Carregar os arquivos")
                .show();
    });

}

It works this way because that’s what you have coded. You are replacing “coletaArquivo” collection every time, after every upload:

    coletaInventarioDc.getItem().setColetaArquivo(listaArq);

As you are saying that these items are displayed in the Grid table, I suppose there is some nested collection container connected to the Grid.
You should inject this container to the screen controller. And then use getMutableItems() to add new items to the collection, like suggested in the manual example:
https://doc.cuba-platform.com/manual-7.2/gui_collection_container.html

@Inject
private CollectionContainer<Customer> customersDc;

private void createCustomer() {
    Customer customer = metadata.create(Customer.class);
    customer.setName("Homer Simpson");
    customersDc.getMutableItems().add(customer);
}

good morning and thanks for the return,

when declaring the variable, the collection is obtained, in sequence, I add more items, all the records being.

should the list not be displayed in full?

List<ColetaArquivos> listaArq = getEditedEntity().getColetaArquivo();

Hi,
When you are using getMutableItems(), it returns you a “magical” collection which listens for adding / removing items and notifies connected UI components about that.
When you are changing collection directly - UI components are not notified that new items were added.

1 Like

simply perfect

after a long time, i had time to return to this project, the proposed solution is simpler and 100% functional.

Thanks a lot for the help.