10
votes

I have a set of entities that connect to each other forming a cycle i.e., parent entity P has two one-to-many relationships with two child entities C1 and C2 and each one of these has a one-to-many relationship with another entity A. Entity A realises the association of these entities (C1,C2) and defines the relationship's attributes (it is not just a join table). All the relationships are navigable in both directions.
domain objects
The following question arises from this design: What should be the cascade strategy so that entity A can be persisted/merged, given that you always invoke entity manager operations on the root entity P? Should A be cascade reachable from both paths?

Considerations: It seems that if the application chooses to provide only one cascade path there can be scenarios that throw a TransientObjectException. If it provides both paths then these paths have to make the full cycle as for example C1 could be attempted to be saved through A.

Versions: JPA 2.0, Hibernate core 4.1.7, hibernate-jpa-2.0-api 1.0.1

2

2 Answers

3
votes

I can give you my 2 cents, sorry if my answer is a bit long.

If you have a cascading conflict of this sort, it may be because your cascading approach or domain model is not well defined. I would be careful to generalize a cascade strategy to an overall graph, or an unrelated set of elements.

My advice would be that the cascade strategy should only be employed on data set strongly tied together, and of the same type, like for a class and its (private) inner classes in the java world. The relation between the mother class and its children's should also be exclusive (in UML it is called Not-Shared Association).

Of course you can do otherwise (we all can be lazy), but at the end you may create a web of coupling between your single persistence flow (or persistence configuration) and your business flow. You will have to manage a lot of exceptions, and do a lot of configuration logic around the cascade strategy you previously put (save, update, delete).

The extreme approach would be that some may wants to save only one big root object. Why not? the rest "should persist in cascade". But in fact, this could limit seriously the maintainability of your system. Moreover, you may have to manage the state of your big graph in memory, when loading, saving and merging it.

If you do a web application, or any client-server application, your web workflow should be able to save a limited set of object at each request without having to save everything from the root element. I know I am not responding directly to your question. So lets go back to your example:

Lets say P is a Bank, and C1 and C2 are two Clients, and A is a Product.

I have two easy answers: 1) Each layer can be save separately without any cascading. But it can be done inside the same transaction, and in the same DAO or not if you want.

2) P and C "can" be cascaded. But A must be saved in a different workflow.

This reminds me of a chapter of Peter Coad where he talks about a "Domain Driven Analysis": http://www.petercoad.com/download/bookpdfs/jmcuch01.pdf

This chapter explains how different objects in a graph can be separated in different archetypes. The persistence workflow should not be the same between transactional data and a description, or a "thing". This can help to put in place a better cascading strategy:

 The four archetypes of Peter Coad are:
 - Is it a moment or interval? 
 - Is it a role played? 
 - Is it a catalog-entry-like description? 
 - Otherwise, it's a party, place, or thing.

I hope it helps.

3
votes

Generally a good idea is to cascade only in close associations and only in parent->child (or owner->owned) direction. In your case it would be probably P->C1 and P->C2. Since A doesn't have one obvious parent, it should be saved separately. This can be done as @etienno mentioned in your DAO in a single transaction together with P (and C1, C2). I don't know your domain model, but maybe even A is at conceptual level a separate entity in which case a separate save is even more justified.

Cascading to many objects that are not closely related may in long term make the entire graph, when it gets bigger, unmanageable.

In situations like this it's always good to ask the question "how would you do it without Hibernate". In your case you would probably first save P, then C1, C2 and then A. AFAIK JPA/Hibernate doesn't provide any explicit way to enforce such order, so some things have to be done by you manually.