A way to solve "relationship not marked as cascade PERSIST" error... the importance of @Composition

I’m writing this to keep memory of an issue that took me quite some time to resolve.
I don’t claim this is the right way to solve this issue in each and every case… it just worked for me
Suppose you have the following multi-level schema in a Bus service application:

  • Line
    ** name
    ** waypoints
    ** shifts

  • Waypoint
    ** address
    ** lat
    ** lng

  • Shift
    ** name
    ** timeTable (list of Stops)

  • Stop
    ** arrivalTime
    ** waypoint

All relationships have oneToMany card. and ASSOCIATION type.

Now you’re trying to build a browser-edit screen where you have all the informations about a Line and you can add new Shifts to the list.
You add a tabSheet and Tabs for Waypoints and Shifts
You create a dialog Edit for Shifts with a groupTable for the timeTable and of course dialog Edit screens for Waypoints and Stops.

After deploy, you create a new Line, some Waypoints and you try to create a new Shift.
The first dialog opens, you want to define the timeTable so you click on the Create button to open the Stop.edit screen, you fill-up the form click ok:

BAM!

IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST:

what is happening here?
As @knstvk explains here the shift is not saved in the same persistence context (transaction) as the stops in the timetable so when the last open Edit screen is committed it finds a Shift in a ‘new’ state which cannot be committed since there is no CASCADE relationship between Stop and Shift.
This particular case can be solved if we think that the timeTable is actually a COMPOSITION of Stops so if we annotate it with @Composition we get the cascade persistence and the error is gone!

The topic is open for corrections

2 Likes

Great post, thank you for share!

@expresado You’re welcome!
I’m writing another solution for those cases in which you don’t want to use a Composition

Good day @lucio.rossi75
Went through your post and its helpful.
You ended saying that you writing a solution in cases you don’t want to use composition. Do you have a solution?

Thank you in advance

Hello there @Indileni !
I wanted to write another solution, but it totally slipped out of my mind…
Anyway what I do now is basically prevent the user to commit Association defined entities when the owning side is a new instance not yet committed.
E.g. I disable the associated entities table entirely in initNewItem() method.

Hello, real problem is a conceptual one, according to me.

In Association entities have individual life, that is they exists even if they are not linked, one entity is NOT part of the other. COMPOSITION means just this (with different cardinalities).

So if you use COMPOSITION and NESTED DATASOURCE (stil using cuba release 6.10) problem is “declaratively” solved (in XML DSContext).

If you use COMPOSITION and NOT NESTED DATASOURCE you should write some code to correctly link entities (solved by nested datasource) but transaction is solved by DSContext in same editor and some constraints on tables.

If you use ASSOCIATION and programmatically create new entities the only solution is to programmatically add new entities to DSCONTEXT as @knstvk explained via a DSCONTEXT listener. This is due to the need of committing in a single database transaction (in core module) all modifications to entities introduced programmatically in an Editor with focus on owing side of ASSOCIATION Entity
Which relation use is an analysis problem. Example:

3 entities:
– EVENT (something like a concert),
– EVENTDAYASSIGNMENT (represent presence of a single artist to a concert in specific date),
– EVENTOPERATION (represent modification of presences time by time, necessary to generate XML
communication at every modification of presence).

EVENT contains (COMPOSITION 1:n) EVENTDAYASSIGNMENT which substantially represent calendar presence of a single artist. (in analysis could be a relation (conceptual) with some attribute [DATE], that means same relation between Artist and Event discriminated by date).

OPERATION contains some of Event’s EVENTDAYASSIGNMENT (modified presences), but they are ASSOCIATED to OPERATION because they exists indipendently from OPERATION (but not from EVENT) and some EVENTDAYASSIGNMENT attributes represent OPERATION Information, for example operation type: (ADD or REMOVE presence).

So, instead of creating a new child object of OPERATION you simply link modified EVENTDAYASSIGNMENT, changing their state (for example a boolean property, say AVAILABLE of EVENTDAYASSIGNMENT that could be simply some derived property like: delete_ts != null if you use soft deletion.

Correct relation OPERATION and EVENTDAYASSIGNMENT is ASSOCIATION according to me (even if it could be discussed, for example deleting EVENT cascade delete all EVENTDAYASSIGNMENTS), but this solution generate error in CUBA if you do not use DSContext Listener, so probably short solution is to use composition in CUBA between OPERATION and EVENTDAYASSIGNMENT to solve error or ASSOCIATION with DSCONTEXT listener to correctly represent ASSOCIATION.

Same situation occurs when you open a second editor to modify associated entity for example an Address in a collection Addresses of a Company and you want ADDRESS to have separate life in order to be reusable (for example if you fill it with google services).

Any of you use some tools to represent Analysis (UML2?) or simply sketch on block-note like me?

Thanks in advance,
Fabrizio

I’ll try both solutions and let you now.