in my code, I have an Employee and Task entities, related with ManyToMany relation. When creating new Employee object, I can assign him to existing tasks with empToBePersisted.getTasks().add(existingTask). However, when I persist it, the relation is persisted in databse but only seen from the Employee side. The Task sees it after restarting the app. How can I make it visible immediately after persisting?
Described behaviour is normal. You need to handle both sides when working with bidirectional associations.
Bidirectional relationships between managed entities will be persisted
based on references held by the owning side of the relationship. It is
the developer’s responsibility to keep the in-memory references held
on the owning side and those held on the inverse side consistent with
each other when they change.
In this case, you will need to call existingTask.setEmployee(empToBePersisted) manually.
You can also see this answer for more details.
Related
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,
PROCESSED (column in TRANSACTIONS) set to N
ACTIVE (column in ERRORS) set to 1
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?
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.
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
I have two entities A and B. A has a many-to-one association towards B (A.listOfBs::List). I want to validate before B is persisted if a B was added to an already existing A.
Debugging into our JPA implementation (Hibernate), I learned that it basically cascades the association and while knowing its a parent<->child association only invokes the PrePersist-handler on B and never the BeforeUpdate on A.
Since I do not want to introduce a bi-directional mapping, I would like to know how I can validate A's children during B's persisting phase as A has special requirements (no two childs sharing the same type).
Is there any mechanism I can learn about parent<->child related events revolving around persisting or deleting children of a particular parent?
From debugging into Hibernate (another framework might be different), the persist event is handled solely on a per entity level. There is a mapping between child and parent in the persistence context that is set prior to persisting the entity (see handling/persisting an association). If one is able to access it, it would be possible for the child to notify its parent event even without a bi-directional connection. All this will continue to feel like a hack.
The best solution seems to be to listen to session level events and iterate through the list of managed entities an let those be validated regardless whether those have changed or not.
I've been using JPA 2.0 for a while but, sad to admit, I haven't had enough time to learn it properly. It seems like I lack the basics of how to work with Entity Manager.
Moving one step at a time, I'd like to first ask you about maintaining relationships between mapped entities. Of course I know how to create mappings between entities, different types of available associations (OneToOne, etc.) and how databases work in general. I'm purely focused on maintaining it via Entity Manager, so please do not send me to any kind of general knowledge tutorial :-).
The questions are:
Am I right that as a programmer I'm responsible for maintaining (creating/updating/removing) relationships between instances of entities?
Do I have to always update (set to null, remove from collection, etc.) instances by hand?
Plain SQL can set entities to NULL on deleting, but it seems like JPA can't do such a simple thing. It also seems like a burden to do it manually. Is there a way to achieve that with JPA?
If I have OneToMany relationship and set to NULL the entity on the Many side of the relationship. Then I persist the changes in a Set by saving the entity on the One side. Do I then have to update the entities in the Many side and set association to NULL in each instance? Seems pure silliness for one-directional bindings!
Thanks in advance!
The main thing you need to investigate is the different options you have when mapping on entity. For example in the next piece of code the cascade all option will instruct jpa to delete the child list when the parent is deleted.
#OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.ALL }, mappedBy = "parent")
private Set<Child> events = new HashSet<Child>();
Yes. You maintain the object tree and modify it to look like what
you want.
Yes and no. If you want the entity to reference null, then yes.
For instance, if you are removing one Entity, then you should clean
up any references to it held by other entities that you are not
removing. A practical example: its good practice to let an Employee
know his/her Manager has been let go. If the Employee is going to
stay, it should either have its manager reference nulled out or set
to a different manager, before the current manager can be removed.
If the employee is going to be removed as well, then cascade remove
can cascade to all the Manager's subordinates, in which case you do
not need to clean up their references to the manager - as they are
going away too.
I don't quite understand what SQL is setting to null. Deleting
removes the row in the database, so there isn't anything to set to
null. Cleaning up a reference shouldn't be that difficult in the
object model, as JPA has a number of events to help such as
preremove preupdate etc. In the end though, the problem is with
your java objects. They are just java objects, so if you want
something done, your application will need to do it for the most
part. JPA handles building them and pushing them to the database,
not changing the state for you.
Yes and no. If you set up a bidirectional relationship, you must
maintain both sides as mentioned above. If you set the child's
parent reference to null, you should let the parent know it no
longer has a child, wouldn't you? Your parent will continue to
reference its child for as long as that Parent instance exists. So
even though the database is updated/controlled through the side that
owns a relationship, the object model will be out of synch with the
database until it is refreshed or somehow reloaded. JPA allows for
multiple levels of caching, so it all depends on your provider setup
how long that Parent instance will exist referencing a Child that no
longer exists in the database.
Lets say I Have two entities with a one to one relationship, a person entity and a person detail entity. Are there any advantages to using cascade when I want to save as oppose to making a separate save for each entity?
Would it be different if it was not a one to one relationship?
To get started, one difference would be, if NOT cascade, there will be multiple network calls (N+1 effect). Based on size data, there are lot of other implications you need to worry about too.
We have two entities Entity1 and Entity2, where Entity1 contains set of Entity2,
we already have thousands of entities stored in database of entity type Entity2 which all are referenced from an instance of Entity1, say myEntity.
Now if i add more Entity2 entities to the collection and try to persist myEntity, where newly added entities of Entity2 are already persisted.
My question is how will be the behavior on persist of myEntity , whether existing members of relation will travel to memory and new members will be added or new members are added to database without bringing existing members to memory
If you have thousands of referenced entities, it might be better not to map the relationship and instead only query for it when needed - allowing you to use paging or other mechanisms to reduce the amount of entities read in at a time. It depends on what type of mapping it is, but only the owning relationship needs to be mapped (the one that doesn't have the mapped by) to set the foreign key in the database. Set the Entity2 side to be the owning side if it isn't already.
If this is a M:M with a relation table and doesn't make sense to map from the Entity2 side instead - you could add an entity for the relation table that you would read in the same way. The new entity would have a reference to Entity1, but Entity1 wouldn't reference it, and the app would query for the new entity when it needs to get Entity2s associated to a specific Entity1.
If you want to add new instances to a relation between two already existent entities (a one to many in this case) then you must first fetch from the database the entity that contains the collection; in your case myEntity.
So, when you load that entity you are bringing it to memory. If you had defined the relation between those two as EAGER then all the related entities (the ones in the collection) will be fetched as well at the same time than the parent one. If you, otherwise, had defined the relation as LAZY then the related entities will be loaded when you access the collection (in other, words, when you invoke the getter getXXX method for that collection).
This happens that way because JPA implementations (now I'm thinking on Hibernate) return proxies of the entities instead of actual instances so they can intercept the getter/setter method calls and perform any tracking on the state of the entities.
Right, so now you want to add more instances to the relation. It doesn't matter whether the relation is EAGERor LAZY in this case as you will eventually invoking the getter method of the collection in order to be able to perform add(myNewEntity); on it. So, the already existent entities are in the collection and you are just adding a (probably) untracked entity under the collection implementation semantics.
When persisting myEntity back to the database the JPA implementation will know which instances of the actual collection need either an update, a delete or an insert. If you just added new instances then just insert statements will be issued but you could also remove an entity from the collection or change the state (invoke the setter) of an already existent instance. JPA implementations are able to recognise those operations and issue the appropriate SQL statements to keep the database up to date.