If I have entitys:
Entity1 has list with Entity2;
Entity2 has list with Entity3;
Entity3 has list with Entity4;
What is operations in my code I have to do when I add new Entity4 in DB?
Just set parent for Entity4 and save Entity4?
or
Set parent for Entity4 and save Entity4. Add Entity4 in list of
Entity3 and save Entity3.
or
Add Entity4 in list of Entity3 and save Entity3. And All Entitys will be update.
It really depends on whether the list maintained by Entity3 is set to cascade operations such as PERSIST, MERGE, and DELETE.
If the list is configured to cascade, then all you'd need to do is:
Set the parent of Entity4.
Add Entity4 to its parent's list and merge the modified parent.
If cascade is not configured, then you'd need to do:
Set the parent of Entity4.
Persist the newly created instance Entity4.
Add Entity4 to its parent's list and merge the modified parent.
Now you may ask why must the parent entity of Entity4 have its list updated and subsequently merged in either case?
That is to make sure that both sides of the association are updated correctly and properly point to one another. Its very likely given the scenario that the parent side of the association is already loaded into the persistence context, so adding the child to the database won't refresh and be visible to an already loaded entity unless its refreshed. The easiest solution in this case is to always modify both sides correctly.
public class ParentEntity {
// add child to the parent entity, maintains association
public void addChild(ChildEntity child) {
if ( child.getParent() != null ) {
child.getParent().removeChild( child );
}
child.setParent( this );
children.add( child );
}
// remove child from parent, disassociates association
public void removeChild(ChildEntity child) {
if ( this.equals( child.getParent() ) ) {
child.setParent( null );
children.remove( child );
}
}
}
I usually find it helpful to expose helper methods like the above on my domain models so that my code doesn't need to be concerned with the associations required to be maintained. I also would likely make the setter for the parent entity's list private so that only Hibernate can use it if needed to force strict use of the addChild / removeChild methods.
Related
Lets assume we have a bi-directional One-to-Many relationship between Parent and Child.
I like the idea of model that relationship with a Set, because of it intrinsic nature of disallowing duplicates.
Question:
1) What would be the proper JPA way to update a child in such a situation?
Query the Parent and pass an updated Child into it?
Query the Child directly and just call its setters?
2) Has either way some performance advantages or disadvantages?
#Entity
public class Parent extends AbstractPersistable<Long> {
#OneToMany(cascade = CascadeType.ALL, ... )
private Set<Child> children = new HashSet();
public void addChild( Child child ) { ... }
public void removeChild( Child child ) { ... }
// non-anemic domain model ?
public void updateChild( Child child ) {
// how to update the element in the Set?
}
}
UPDATE:
How to properly write the update method? Since Sets in Java do not have a get method?
To update a Child, you don't need to operate the parent collection.
Thanks to the dirty checking mechanism, once the Child becomes managed in the currently running Persistence Context, every change is picked automatically and synchronized to the database.
That's the reason you don't have an update method in JPA. You only have persist or merge in EntityManager.
So, you need to do the following steps:
You load the Child by id:
Child child = entityManager.find(Child.class, childId);
Do the changes on the Child and you are done:
child.setName(newName);
I'm having trouble getting my merge to cascade down when there are new child objects.
I have a parent object (City) with a set of child objects
#OneToMany(mappedBy = "parent", cascade = { CascadeType.ALL }, fetch=FetchType.LAZY )
public Set<Street> getStreets() {
return streets;
}
And a child object (Street) referencing the parent
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="City", nullable=false)
public City getParentCity(){
return parentCity;
}
And I'm running a daily routine to dump information into the database. All well and dandy until JPA runs into a new street. When it gives me an EntityNotFoundException. I'm assuming that it means there's no street already in the database with the new ID. From what I've read it's because the child is the actual owner of the transaction, but I need it to persist the new object if it runs into this "EntityNotFound" exception
I'm not using an auto-generated primary key, It's a key that already exists in the dataset. Currently I have access to a micro-service that uses em.merge() on the parent object.
I see that there's an option to delete objects that no longer exist (orphanRemoval); is there a way to add new ones?
Thanks
I dont know if it is the case, but with me, only works if I put the hibernate's annotations above the variable attributes themselves, not on getter methods.
What worked for me was 'manually' saving first the street, and then the City.
Say there are two entities: Parent and Child, with #OneToMany mapping from Parent to Child.
class Parent {
#Column(name="id")
private Long id;
#OneToMany(cascade=CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "parent_id", referencedColumnName = "id")
private List<Child> children;
}
class Child {
#Column(name="id")
private Long id;
#Column(name="parent_id")
private Long parentId;
}
As you can see, in my case, the Child table stores a foreign key to the Parent's primary key. But I don't want that as bi-directional mapping in my Child entity.
The issue arises now is, I'm unable to set the parent_id in Child instances.
I've created instances like this:
Parent parent = new Parent();
parent.setChildren(Lists.newArrayList(new Child(), new Child()));
parentDomainService.save(parent);
Assuming that there is cascading on Parent end. This approach saves the Parent first, then saves the Child instances. And then it runs the update query on child instances to update the parent_id, as I see from the Hibernate show_sql logs. But surprisingly, after update query, I see for some of the child, the parent_id is null. That was surprising to me.
So, I went to handle that thing manually, and removed cascading. Then I saved the entities like this:
Parent parent = new Parent();
parent.setChildren(Lists.newArrayList(new Child(), new Child()));
parent = parentDomainService.save(parent);
for (Child child: parent.getChildren()) {
child.setParentId(parent.getId());
}
childDomainService.save(parent.getChildren());
This one bounced back on me with following exception:
org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.somepkg.Child
I've seen many questions on SO with that exception, and I know there are many out there, but almost all of them are dealing with bi-directional mapping, or uni-directional mapping with JoinTable. Neither of them suits my situation.
Any lights on this? I'm out of options.
P.S.: The actual scenario I'm dealing with requires saving huge amount of data. E.g.: 50000 parent records, and 250000 Child records. That is why I don't want bi-directional mapping. Because saving Child will do create a query with join table in the back-end.
I'm mostly interested in solution, wherein I don't have to fire query twice on Child table. As that is happening in my current application, and that is hampering the performance.
When you remove cascading the parent does not persist the referenced child elements and at
parent = parentDomainService.save(parent);
the parent references the "unsaved transient" child instances and therefore throws the exception. If you first save the parent and then add the children:
Parent parent = new Parent();
parent = parentDomainService.save(parent);
parent.setChildren(Lists.newArrayList(new Child(), new Child()));
for (Child child: parent.getChildren()) {
child.setParentId(parent.getId());
}
childDomainService.save(parent.getChildren());
then the exception will not be thrown.
Within a complex usertransction (ut) we have to find a JPA entity, add a new child and find the parent entity again. We observe, that the list of children is incomplete when we fetch the parent inside the transaction and complete as expected after the transaction commits. Everything is single threaded and uses the same entity manager instance.
Are we missing something obvious that would explain that behaviour?
ut.begin();
// find the parent entity
ParentEntity parent = em.find(parentKey);
assertEquals(parent.getChildren().size(), 1); // as expected
// add one child
ChildEntity child = createChild(); // create child for the parent
child.setParentRef(parent.getRef());
em.persist(child);
em.flush();
// find the parent entity again
ParentEntity parent = em.find(parentKey);
assertEquals(parent.getChildren().size(), 1); // UNEXPECTED! Should be two...
ut.commit();
// find the parent entity again
ParentEntity parent = em.find(parentKey);
assertEquals(parent.getChildren().size(), 2); // now I see 2 children. After committing
The the relation on parent is defined like:
#OneToMany(orphanRemoval=true, cascade=CascadeType.ALL, fetch=FetchType.LAZY)
#JoinColumn(name="parent_ref", referencedColumnName="ref")
List<ChildEntity> children = new ArrayList<ChildEntity>();
This is completely expected. You set the parent of the child, but didn't add the child to the parent list. And all is in a single transaction, so the parent returned by the second call to em.find(parentKey) comes directly from the first-level cache and is thus the exact same object as the one returned by the first call.
It's your responsibility to maintain the coherence of the object graph in a transaction. See http://docs.jboss.org/hibernate/core/4.3/manual/en-US/html_single/#tutorial-associations-usingbidir
You need to add new child to the parent collection:
ChildEntity child = createChild();
child.setParentRef(parent.getRef());
parent.getChildren().add(child);
em.persist(parent);
em.persist(child); // only required if the persist does not cascade to children
em.flush();
I have Parent->Child bidirectional relationship as follows...
class Parent{
#OneToMany(mappedBy="parent", fetch = FetchType.EAGER)
Collection<Child> children;
}
class Child{
#ManyToOne
#JoinColumn(name="PARENT_ID")
private Parent parent;
}
When i delete child explicitly, and after that load its parent (with all children) i get previously deleted child in children collection of parent... JPA provider is Hibernate...
Child child= childRepo.findOne(CHILD_ID);
childRepo.delete(child);
childRepo.flush();
// next, returns collection without deleted child
Collection<Child> children= childRepo.findAll();
Parent parent = parentRepo.findById(PARENT_ID);
/// next, returns collection including deleted child
Collection<Child> parentChildren = parent.getChildren();
I don't understand what is the problem? Every find* method executes select (at list, those SELECTs are logged in console) , but they returns different results...
Your ManyToOne is EAGER (by default). Your OneToMany is also EAGER (you explicitely marked it so). So, when you get a child in your first line of code, JPA also loads its parent, and all the children of the parent.
Then you delete the child, but you don't remove it from the parent's chidren. And since the parent's collection of children is already loaded, the deleted child is still in the collection.