I have a Parent and Child entity. Child has a Manytoone annotation to reference Parent. The use case is that when i load a child by findById and get the parent out using the getter. then If I update the parent, I am able to save the parent. I do not want the parent to be updatable which is pulled out from a child.
I want only to update parent if I directly find it by id and change attribute and save.
I know hibernate has no info when i do getParent() from child that it got it from child and not find by id and makes an update to parent.
I tried Immutable annotation on ManytoOne on Parent but it does not prevent an update to be fired.
Is there any way that I can make the parent pulled out from a child non-updatable?
feel free to ask any clarifications.
The problem you are experiencing, in fact belongs to the Hibernate Session. It has nothing with Cascading. Because the session is designed to be a unit-of-work for us.
Whenever you access an object 1) by ID or 2) by reference, this object is (from that moment) "cached" in the Session. It is one instance shared accross all the handling during the Session life-time.
So, once, the Parent is loaded into session, and you are amending it anyhow, and later Session is flushed - any changes to Parent are persisted.
You can call session.evict(parent) explicitly, to avoid changes propagation during the Flush.
But It seems to me a bit weird, that you are changing the Parent (accidently...maybe) while working with child and not willing to store these changes. Other words, maybe solution is to change the approach.
You can call EntityManager.detach() on the parent that you obtain through the getter. Any change made to the detached entity will not be synchronized to the database.
Related
I have a parant entity Project and child entity Task(onetomany relationship). I would like to know how update child right. All examples I found are with one table.Thanks
What exactly do you need here? if you want to update the child of current parent then iterate through the child list / set and then modify the appropriate child within that for each loop. You could also update your parent too. so here when you flush / commit your transactions your parent table and the child tables as well get updated successfully.
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.
In a #OneToMany relationship if I want to remove a child, do I need to explicitly delete that child from parent's collection as well or just deleting the child will suffice?
For instance, Person and Phone. Each person has many phone numbers. If I want to delete one phone number from a person is this enough:
EntityManager.remove(phone);
Or I need to to this beforehand:
Person.getPhone().remove(phone);
Not to mention, the CascadeType is set to MERGE.
You need to remove the Phone explicitly from the phones collection, it's not enough to remove it with the EntityManager.
From the other side, it might be sufficient to use orphanRemoval, so if you remove an entity from a collection, it gets automatically deleted. Something like:
#OneToMany(mappedBy="person", orphanRemoval="true")
private List<Phone> phones;
See also: http://docs.oracle.com/cd/E19798-01/821-1841/giqxy/index.html
Cascade.REMOVE only removes the child entity if the parent entity is removed as well. Cascase.MERGE has nothing to do with this problem.
Not sure if MERGE is enough to get entities deleted cascaded, you will probably have to also define DELETE cascading and depending on how the data is mapped (with or without a secondary table in between) it might even be necessary to apply orphan removal too.
If you don't apply cascading for deletion but rather use a JPA query or an entityManager.remove(), then it is certainly a good idea to manually evict it from the oneToMany collection as well. The reason is simple: you may manually remove it from the database, but that doesn't mean it automagically gets removed from the collection too so for the lifetime of the parent entity, it will still be referencing an entity which is not supposed to exist anymore. Things get weird when you then also accidentally change the state of said entity.
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.
I am trying to delete an entity but when I use the entity manager i do not have errors but te data is still in the database, I am using cascade all, independently of this, I am deleting its parent reference as
Parent parent = child.getParent();
parent.getChildren().remove(child);
entityManager.remove(child);
entityManager.merge(parent);
Does anyone know a way to do this?
This can have several reasons:
You are not removing the parent reference from the child - child.setParent(null);
This is important since the parent id is probably stored inside the child table.
The removal is not committed yet when you are calling merge. This should not be an issue, but if the first idea does not help I would try and put the deletion and the merge in separate transactions.
I assume that you are using CascadeType.ALL on the parent only.
Edit according to Chris comment because it was indeed not clear at all.
1st, you have to understand than an entity instance can have many states. http://openjpa.apache.org/builds/1.2.3/apache-openjpa/docs/jpa_overview_em_lifecycle.html
Merge is here to attach an entity to the current entityManager. Once it's managed, all modification you will make on the entity will be persisted in database when the EntityManager is flushed. This happen normally at the end of the current transaction (or when you call flush manually or when the entityManager estimates that it's necessary).
If your entities have been retrieved from the DB inside the current transaction, you don't need to call merge as they already are managed.
Assuming this
You just need to call
entityManager.remove(child);
Or
parent.getChildren().remove(child);
if you've configured orphan removal on the relationship (in parent).
This will so be automatically flused to the database when the current transaction will be flushed and NOT at the method call.
Otherwise you can force flushing by using entityManager.flush()but it's a bad practice. Anyway you won't see the change in the database until the transaction is commited / closed.
If you want to remove a child using entityManager.remove(child) you must ensure to apply associated modification on parent entity (using parent.getChildren().remove(child) and not entityManager.refresh(parent)) if your parent instance is currently managed (means it actually exists an instance of your parent in memory, because you've retrieved it or because you've called child.getParent() or because it was eagerly loaded when you retrieved the child.)