0

The standard Jspresso action cloneEntityCollectionFrontAction allows to duplicate the selected rows in a table. The duplication is limited to the current model and do not take account of collections if exist (ie : the collections are not automatically duplicated)

how to deeply duplicate an entity with all of its collections ?

Second related question : I tried to write by myself an action in order to realize the duplication of the collections. Below a part of the action I wrote :

Offer newOffer = bc.getEntityFactory().createEntityInstance(Offer.class);
Offer clonedNewOffer = bc.cloneInUnitOfWork(newOffer);

clonedNewOffer.setCustomer(curOf.getCustomer());
clonedNewOffer.setEndApplicationDate(curOf.getEndApplicationDate());
clonedNewOffer.setName(curOf.getName());
clonedNewOffer.setStartApplicationDate(curOf.getStartApplicationDate());

I called the getter and setter for each property which is not satisfying because if I add new property or collection to the model, the method must be manually updated.

Is there a way to write a more smart / flexible method ?

Hi Vincent, Regarding the answer you made and your latest proposal, I changed my backend with the following one :

Offer newOffer = bc.getEntityFactory().createEntityInstance(Offer.class);
Offer clonedNewOffer = bc.cloneInUnitOfWork(newOffer);

CarbonEntityCloneFactory.carbonCopyComponent(curOf, clonedNewOffer, bc.getEntityFactory());

bc.registerForUpdate(clonedNewOffer);

But the registerForUpdate failed due to Data constraints are not satisfied error.

I checked the Id property of the clonedNewOffer and the Id is already the same than curOf Id property. I understand the meaning of a "carbon copy" which is a strictly copy of all the properties, so, from a backend,

how could I duplicate an entity in order to create a new one ?

Ygor
  • 35
  • 7

1 Answers1

1

Both CloneComponentCollectionAction and CloneComponentAction perform the actual component and entity cloning using a configurable strategy that implement IEntityCloneFactory. Jspresso provide 3 implementations of this interface :

  • CarbonEntityCloneFactory that deals with scalar cloneable properties but ignores all relationships. It's almost never used directly by application code.
  • SmartEntityCloneFactory inherits from CarbonEntityCloneFactory and deals with relationships the following way :
    • clone the references if they are compositions or assign the same references to the clone.
    • add the cloned component to the same collections than the original belongs to.
  • HibernateAwareSmartEntityCloneFactory inherits from SmartEntityCloneFactory and deals with lazy initialized properties. This is the implementation that is used by default if you use an Hibernate backend.

As a rule of thumb, you can expect the SmartEntityCloneFactory to perform what you expect about references but ignore dependent collections in order to avoid too deep recursive cloning; so what you've experienced is per-design. If you feel like there is room for improvement, feel free to open a feature request on the Jspresso GitHub. Thinking about it, we could maybe do better about composition dependent collections.

When you want to deal with deeper cloning than what's provided by the SmartEntityCloneFactory (or HibernateSmartEntityCloneFactory), the way to go is to create your own cloning strategy. Of course, you can inherit the default strategy and complete the cloning by overriding the cloneEntity method by calling the super implementation and deal specifically with the collections you want to clone.

Once your strategy is implemented, just inject it either globally in the application by replacing the default one, i.e. :

bean('smartEntityCloneFactory', class: 'your.CustomEntityCloneFactory',
     parent: 'smartEntityCloneFactoryBase')

or specifically on one of the clone actions of your application by injecting your custom strategy on the action, e.g. :

bean('myCustomEntityCloneFactory', class: 'your.CustomEntityCloneFactory',
     parent: 'smartEntityCloneFactoryBase')

action('customCloneAction', parent: 'cloneEntityCollectionFrontAction',
       custom:[entityCloneFactory_ref: 'myCustomEntityCloneFactory']
)

Regarding your second related question, if you are inside your entity clone factory implementation (or have access to an instance of it) and want to clone an entity or a component using the strategy, just call the cloneComponent or cloneEntity method.
If you just want to copy all the scalar properties of an entity or component on a clone and don't have access to a clone factory, you can use the following static utility method :

CarbonEntityCloneFactory.carbonCopyComponent(IComponent, IComponent, IEntityFactory)

Using the above method will solve your implementation robustness.

  • I wrote a custom stategy in order to clone the entity and its collection. When I iterate on the collection and call the cloneEntity method for each element of the collection, the error below is raised on the second element of the collection : **ConcurrentModificationException** – Ygor Jun 05 '15 at 14:33