I have two entity like this:
#Entity
public class Parent {
#Id
#GeneratedValue
private Long id;
#LazyCollection(LazyCollectionOption.EXTRA)
#OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Child> childs = new ArrayList<>();
...Getter & Setter
}
#Entity
public class Child {
#Id
#GeneratedValue
private Long id;
#ManyToOne(optional = false)
private Parent parent;
...Getter & Setter
}
When remove child from parent, hibernate will query all children of this parent:
parent.getChilds().remove(child);
I have 100000+ child link to the parent. query all children is very slow and unacceptable.
How to fix this question or use other way?
Hibernate has to fetch all the children in order to prepare proper SQL - needs to know IDs of children elements to delete.
So there is no way to do it using ORM in way you would like to use it, but if you ask yourself a question, how would you do this in plain SQL obvious solution would be
DELETE c FROM child c WHERE c.parent_id=parentId
Hibernate is able to generate such query, but it is done via HPQL. So if you have delcared bidirectional relationship between parent and child and you have child has parent property, then you could do the following using HPQL:
Query q=session.createQuery(`DELETE c FROM chilc WHERE c.parent=:parent`);
q.setObject("parent",parent);
q.execute(); // q.executeUpdate?
This way you wil delete all the children of parent without fetching them.
DISCLAIMER:
Given query may not be 100% accurate because i dont remember Hibernate's Session API anymore, but the overall glance on the solution is given and 100% valid. Just reffer to Hiberrnate's docs for details.
Use the sessions's delete method without using the Parent and cascading the update:
sesionFactory.getCurrentSession().delete(child);
Update
It is a good practice to perform merge before a delete like this to make sure its managed:
sesionFactory.getCurrentSession().merge(child);
Related
On a bidirectional relationship beetwen two entities (a ControlTable made up of ControlSteps), i'm simply trying by different ways to request a ControlTable by knowing the collection ControlSteps of it. I know that it's not recommended to have this bidirectionnal mapping but i need to know each childs of a parent, and the parent for each child.
I configured it like this in ControlTable class:
#OneToMany(mappedBy = "controlTable",cascade = CascadeType.ALL, fetch=FetchType.EAGER)
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Fetch(FetchMode.JOIN)
private Set<ControlStep> controlSteps;
And like this for ControlStep class :
#ManyToOne(optional=false, fetch=FetchType.LAZY)
#JoinColumn(name="ctrl_table_id", referencedColumnName = "id")
private ControlTable controlTable;
When i use the default JPA query findAll(), it's not working to get the list of ControlTables (or only one) because it's requesting recursively the parent in the child's parent (infinite response).
In another way, itried to put all in LAZY loading, with an HQL query fetching the childs, but the result is the same.
Do you have any idea of how to get these collections without problems?
Thank you very much by advance
Found it. The problem was Spring Data Rest and JSON transformation, for more details :
Infinite Recursion with Jackson JSON and Hibernate JPA issue
I am using hibernate with JPA annotations for relationship mapping.
I have three entities in my code User Group & User_Group
User & Group are in a ManyToMany relationship.
User_Group is a kinda bridge table but with some additional fields. So here is the modified mapping code.
User
#Entity
#Table(name = "USERS")
public class User {
#OneToMany(mappedBy = "user")
private Set<UserGroup> userGroups
}
Group
#Entity
#Table(name = "GROUPS")
public class Group {
#OneToMany(mappedBy = "group")
private Set<UserGroup> userGroups
}
UserGroup
#Entity
#Table(name = "USERS_GROUPS")
public class UserGroup {
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "USER_ID")
private User user;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "GROUP_ID")
private Group group;
}
When I set the user & group object to the usergroup & save it.
User user = new User("tommy", "ymmot", "tommy#gmail.com");
Group group = new Group("Coders");
UserGroup userGroup = new UserGroup();
userGroup.setGroup(group);
userGroup.setUser(user);
userGroup.setActivated(true);
userGroup.setRegisteredDate(new Date());
session.save(userGroup);
Things work fine. With CascadeType.ALL the group object & user object are updated too. But when I delete the userGroup object. The child object are deleted too.
Deletion of child objects is a strict no no.
There is no CascadeType.SAVE-UPDATE in JPA, which just does save or update but no delete. How do I achieve this.
If I remove the CascadeType.ALL from the mapping the child objects don't get updated & I need them to be updated.
SAVE_UPDATE is for save(), update(), and saveOrUpdate(), which are 3 Hibernate-proprietary methods. JPA only has persist() and merge(). So, if you want to use cascading on Hibernate-proprietary methods, you'll need to use Hibernate-proprietary annotations. In this case, Cascade.
Or you could stop using the Hibernate Session, and use the standard JPA API instead.
CascadeType.ALL includes CascadeType.REMOVE too.
The solution is to use all CascadeType.* you need except CascadeType.REMOVE, like so:
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.MERGE}))
in your UserGroup definitions.
It's almost always a code smell when propagating from child to parent entity, it should be the other way round.
From Cascading best practices:
Cascading only makes sense only for Parent – Child associations (the
Parent entity state transition being cascaded to its Child entities).
Cascading from Child to Parent is not very useful and usually, it’s a
mapping code smell.
From Hibernate best practices:
Avoid cascade remove for huge relationships
Most developers (myself included) get a little nervous when they see a
CascadeType.REMOVE definition for a relationship. It tells Hibernate
to also delete the related entities when it deletes this one. There is
always the fear that the related entity also uses cascade remove for
some of its relationships and that Hibernate might delete more
database records than intended. During all the years I’ve worked with
Hibernate, this has never happened to me, and I don’t think it’s a
real issue. But cascade remove makes it incredibly hard to understand
what exactly happens if you delete an entity. And that’s something you
should always avoid. If you have a closer look at how Hibernate
deletes the related entities, you will find another reason to avoid
it. Hibernate performs 2 SQL statements for each related entity: 1
SELECT statement to fetch the entity from the database and 1 DELETE
statement to remove it. This might be OK, if there are only 1 or 2
related entities but creates performance issues if there are large
numbers of them.
I am using Hibernate 3.5 JPA and have doubt about specific behavior by this implementation.
We have Parent which has two child with below property
#OneToMany(cascade = CascadeType.ALL, mappedBy = "parent", fetch = FetchType.LAZY)
private List<ChildOne> childOneList;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "parent", fetch = FetchType.LAZY)
private List<ChildTwo> childTwoList;
And ChildOne with below property for referencing Parent (Assume even ChildTwo with same code as below)
#JoinColumn(name = "PUID", referencedColumnName = "PUID")
#ManyToOne
private Parent parent;
I am persisting parent object using JPA EntityManager.merge(PARENT), at this time I don't have child of this specific parent, however this child table has many records which are NOT related to this parent record. (Linked child record will be created after some time)
parent.setChildOneList(null);
parent.setChildTwoList(null);
em.merge(parent);
What happens is, on EntityManager.merge, it first fire select query with "left outer join" on childs (which fetches all unrelated records) and then apply insert/update on parent record. SQL queries in log like.
Hibernate: select parent0_.PUID as PUID96_1_,childoneli1_.PUID as PUID96_3_,childoneli1_.CHILD_UID as CHILD1_3_,childoneli1_.CHILD_UID as CHILD1_97_0_,childoneli1_.PUID as PUID97_0_ from PARENTT parent0_ left outer join CHILDONE childoneli1_ on parent0_.PUID=childoneli1_.PUID where parent0_.PUID=?|#]
Hibernate: insert into PARENTT (PUID) values (?)|#]
This is temporarily solved by removing the Cascade.ALL. Now, I may need this in future so, wanted to know any pointers on this behavior by Hibernate JPA implementation. (Annotation Import has all javax.... no Hibernate specific).
If this is what expected from Hibernate JPA then and please point me on how to correct it with Cascade.ALL and without unnecessary "left outer join" select on merge call.
I need some help.
I have two classes with onetomany relationship:
#Entity
public class Parent extends Model{
#Id
public Long id;
#OneToMany(fetch = FeatchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
public List<Child> children;
}
and
#Entity
public class Child extends Model{
#Id
public Long id;
}
So when i call remove() Child entity do not remove from DB.
Parent parent = Parent.find.byId(id);
parent.children.remove(parent.children.get(0));
parent.save();
And next time I find.byId - all children is there, like they never been deleted :(
Play 2.0.4, inMemory database.
Please make me know if any other informaition is needed.
I did some more tinkering with this and realized why this doesn't work.
Since you have a unidirectional relationship without any join table, to actually delete the association would mean to delete the corresponding child record (or at least update the parent_id column). It is probably a good thing that EBean doesn't do this, because it doesn't know/checks if perhaps some other table has a foreign key relationship to Child.
What you can do is to explicitly indicate that no other table has a FK relationship to child, that it is "owned" by Parent and it is OK that Ebean removes the entity altogether when removed from the relationship. This is done by adding a #PrivateOwned annotation on the children attribute of Parent.
This post tipped me of: https://groups.google.com/forum/?fromgroups=#!topic/ebean/dXPWpJCQkj8
Ok I have a 1-to-many mapping between a parent and child class. I can save the parent and it will automatically save the children objects, but problem is when doing a SELECT on the parent class. It seems that I'm getting a Parent object for every Child object in database table.
So if I save 1 parent object with 2 child objects, when I use Hibernate select Criteria I get 2 Parent objects!!!
All I want is for Hibernate to return 1 parent object with its 2 child objects inside the Set child field.
My mappings must be wrong I guess. Can someone please help with this?
class Parent{
Long parentId;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "parent_table_id", nullable = false)
Set childs;
....
}
class Child{
Long childId;
}
Note: the "parent_table_id" references the Parent primary key. Also this value is not mapped into any Parent or Child object. I manually insert this value and only use it in the #JoinColumn annotation.
Ok I'm new to this JPA stuff but it seems that Hibernate is automatically inserting the values for field "parent_table_id" in the Child table when I save a Parent with Child objects.
Could this be causing the problem?
Your mapping looks fine, it sounds like what you actually need is:
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
as you're performing an inner join that results in more than one result row being returned in your criteria query.