add new entity to table

Beginner question… I have a screen with a GroupTable that displays entities from a GroupDatasource. If I programatically create a new entity (detached), what is the correct way to add it to the table and have it persist when the screen is closed? Do I just add it to the table using addItem()? Or do I add it to the Datasource (with addItem)? Or something else?
Thanks
Eric

Let me clarify my question a bit. What I’m trying to do is this: the user selects a row in a grouptable and clicks on a button called “Recycle”. I want onRecycle to create a duplicate of the existing row and open it for editing. Specifically, my plan is to have the controller do the following:

  1. Use getSingleSelected to read the entity for the existing row
  2. Make a duplicate entity by serializing and deserializing, then inserting random UUIDs for the new entity and its referenced entities
  3. Add the new entity to the datasource using addItem
  4. Use setSelected to select the new row
  5. Open the standard edit action
    Does this sound reasonable? Will the new object be attached and persisted?
    Thanks
    Eric
1 Like

Hi

attached you will find an example of the “recycle” function or “copy” as i would have called it.

Basically, i create a Action in the Controller that does the copying like this:


class RecycleAction extends ItemTrackingAction {

        protected RecycleAction() {
            super("recycle");

            setCaption("Copy / Recycle");
            setIcon("icons/copy.png");
        }

        @Override
        public void actionPerform(Component component) {
            Customer customer = customersTable.getSingleSelected();
            Customer newCustomer = copyCustomer(customer);

            showNotification("Customer " + newCustomer.getInstanceName() + " created...", NotificationType.TRAY);
        }

        Customer copyCustomer(Customer customerToCopy) {

            Customer newCustomer = metadata.create(Customer.class);

            newCustomer.setBirthday(customerToCopy.getBirthday());
            newCustomer.setName(customerToCopy.getName() + " - copy");
            newCustomer.setCustomerType(customerToCopy.getCustomerType());

            customersDs.addItem(newCustomer);
            customersDs.commit();

            return newCustomer;
        }
    }

The action gets attached in the init method of the controller to the table customersTable.addAction(new RecycleAction());.
The copying of the data uses the Metadata interface from CUBA to create instance of it. It sets the attributes and attaches the new instance to the datasource. After that the datasource gets committed.

I hope this was what you wanted to achieve.

Bye
Mario

cuba-example-duplicate-entry.zip (35.6K)

1 Like

Thanks so much! Worked very well. Yes, “Recycle” is a bit of an odd choice, but it’s from an old code and is tradition.

Mario,
So, I tried integrating your example into my code and came across a problem that seems to involve the security system. The code is shown below. Why I try to run this, the screen becomes darkened with a message saying “Access Denied”. In chasing this with a dubugger, the error occurs in the WindowManager when it tries to access the “isNew” attribute of the entity. Since there are no permissions against accessing the entity, I’m guessing that the security system is failing to recognize the entity in some way.
As to structure, the Visit entity has a one-to-one relationship with Mse. In turn, Mse has a many-to-one relationship with Alertness and a many-to-many relationship with Affect. I had to do the UUID randomization, otherwise the persistence failed to recognize the copied entity as a new object.
Any suggestions as to where I’m going wrong?
Thanks
Eric


class RecycleAction extends ItemTrackingAction {

        protected RecycleAction() {
            super("recycle");

            setCaption("Copy / Recycle");
            setIcon("icons/copy.png");
        }

        @Override
        public void actionPerform(Component component) {

            Visit visit = (Visit) visitsTable.getSingleSelected();
            Visit newVisit = null;

            try {
                newVisit = (Visit) ObjectCloner.deepCopy(visit);

                newVisit.setId(UUID.randomUUID());  
                newVisit.getMse().setId(UUID.randomUUID());

                visitsDs.addItem(newVisit);
                visitsDs.commit();   // same problem with or without this line


            } catch (Exception e) {

            }

            editAction.setAfterWindowClosedHandler((window, closeActionId) -> {
                visitsDs.refresh(); 
                editAction.setAfterWindowClosedHandler(null);   // remove so won't trigger on next edit button press
            });

            visitsTable.setSelected(newVisit);
            editAction.actionPerform(visitsTable);
}

public class ObjectCloner {

    // so that nobody can accidentally create an ObjectCloner object
    private ObjectCloner() {
    }

    // returns a deep copy of an object
    static public Object deepCopy(Object oldObj) throws Exception {
        ObjectOutputStream oos = null;
        ObjectInputStream ois = null;
        try {
            ByteArrayOutputStream bos =
                    new ByteArrayOutputStream(); // A
            oos = new ObjectOutputStream(bos); // B
            // serialize and pass the object
            oos.writeObject(oldObj);   // C
            oos.flush();               // D
            ByteArrayInputStream bin =
                    new ByteArrayInputStream(bos.toByteArray()); // E
            ois = new ObjectInputStream(bin);                  // F
            // return the new object
            return ois.readObject(); // G
        } catch (Exception e) {
            System.out.println("Exception in ObjectCloner = " + e);
            throw (e);
        } finally {
            oos.close();
            ois.close();
        }
    }

}

Hi Eric,

I think the problem is that when you create the new instance by re-serializing an existing entity, the new instance is in Detached state as its original. So the editor screen thinks that it should reload this instance from the database by ID, and due to there is no such entity in the database it throws the exception. It says “Access denied” because the same occurs when a user has security constraints preventing him to read a particular instance.

Try to do the following with your new instance after deserializing:

BaseEntityInternalAccess.setNew(entity, true);
BaseEntityInternalAccess.setDetached(entity, false);