worldbeater
worldbeater

Reputation: 351

What is the significance of being the realtionship owner and how does CASCADE work with the two sides in JPA?

I have two tables. Transactions and Errors. There exists a One-To-Many relationship between Transactions and Errors. This is a bi-directional relationship and Errors is the owning side as @JoinColumn is specified in the Errors class. I want to understand what exactly does it mean to "OWN" the relationship. Say at the moment I have,

Scenario 1: Now lets suppose we execute the below code.

transactions.setProcessed("Y");
errors.setActive(0);
transactions.setErrors(errors);
entityManager.merge(transactions);

I understand that the PROCESSED field will get set to "Y" in TRANSACTIONS but will the ACTIVE field in ERRORS also get set to 0 or not given that transactions IS NOT the OWNING side of the relationship?

Scenario 2: On the other hand, if we execute the below:

errors.setActive(0);
transactions.setProcessed("Y");
errors.setTransactions(transactions);
entityManager.merge(errors);

I understand that the ACTIVE filed in ERRORS will be set to 0 but will the PROCESSED field in TRANSACTIONS also be set to "Y" given that ERRORS IS the OWNING side of the relationship?

How do JPA cascade types tie into scenarios like this?

Upvotes: 0

Views: 210

Answers (2)

Chris
Chris

Reputation: 21145

In a non-bidirectional relationship, you define a mapping. When you make a change to that mapping, it is obvious what will happen - a foreign key will get updated. Because there is only one mapping, there can be no conflict (many JPA providers will throw errors if they detect you have more then one writable mapping for a field).

With a bidirectional relationship, this control is less obvious. In your transaction-Error bidirectional relationship, assume it is a OneToOne bidirectional mapping, and Transaction1 is set to point to Error1 and vis versa. Lets say your application determines that Transaction1 should be pointed to Error2 instead, and changes the reference. If Error1's reference to Transaction1 isn't corrected to reflect this situation, there is a problem for JPA in determining what value to put into the foreign key. This is where ownership comes into play. The owning side is considered the writeable mapping, and changes to it control the foreign key fields. In a OneToMany, the owning side is usually the ManyToOne back reference because it is more natural since the foreign key is in the table holding the ManyToOne anyway. If you make a change to add an Error to a Transaction but do not change the Error to also reference that Transaction, your object model will be out of sync with what goes into the database - The foreign key in Error will not be changed, but the transaction object will show an error in its list until it is refreshed or reloaded from the database.

Cascading is something unrelated to ownership. It just means the operation (persist, merge, delete, refresh) applies to the entity referenced by the relationship. If using cascade.all an call em.refresh(transaction), the transaction and all referenced Errors will be refreshed from the database. Any relationships Error then has that have a cascade setting of ALL or REFRESH will also get refreshed and so on. JPA should detected that it has already refreshed the referenced Transaction instance if you place it on the back references, but why risk it. Generally, cascade options should only be placed on mappings where they are required to avoid unintended consequences. If you aren't sure if it is needed, leave it off until you are sure. Things like lazy fetching and other optimizations can cause all sorts of strange and hard to find bugs when someone goes and puts a cascade refresh everywhere.

In your example, you might put a cascade merge on the root entity that your application will be passing around. Any changes made to that graph then are easily picked up with a single merge call without having to call merge on each individual leaf. How your model is built and serialized though will affect the merge, so generally cascade options are put only on the root->Leaf relationships to avoid issues where the root -> leaf -> root' where root != root'. If you have cascade merge on both sides, the state of root' might overwrite your changes in root.

Upvotes: 2

codeLover
codeLover

Reputation: 2592

When we say that Errors is the owning side, that means foreign key of the relationship lies within the Errors table(which you are doing via @JoinColumn). Thus, the owning side of the relationship is the one in which reference column of the other entity will be present. You can define the inverse side of relationship by specifying @OneToMany in the Transactions entity.

Now comes the second part of your question regarding the update of transactions and errors. In my view you can update List associated with a transaction by applying appropriate mode of cascade(persist,delete etc) which means you can specify in your Transaction entity @OneToMany(cascade=CASCADETYPE.MERGE) while specifying the inverse relationship. In this way, if whenever you will update a transaction row, corresponding error rows can also be updated.

However, I don't think it is a good practice to cascade in the other way ie if you update child entity the parent entity should also get updated as it may lead to many data inconsistencies

Upvotes: 1

Related Questions