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
Related
I have 2 classes (ONE to MANY mapping).
Parent class:
#Entity
#Table(name = "parent")
public class Parent{
#Id
private int ParentId;
#OneToMany(cascade=CascadeType.ALL, mappedBy = "parent")
private List<Child> childEntities;
Child class:
#Entity
#Table(name = "child")
public class Child{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int childId;
#ManyToOne(cascade={CascadeType.PERSIST, CascadeType.DETACH, CascadeType.REFRESH, CascadeType.MERGE})
#JoinColumn(name="parent_id")
private Parent parent;
When I want to save parent object do database with parentRepository.save(parent) everything is right. Parent and Child are saved into database. The problem is, when I want to perform parentRepository.save(parent) on parent that is already in the database (JPA will do update - it updates parent table, but in child table it inserts new rows). The parent table is right, but in child table I have duplicate values. Primary Key in child is SERIAL (I use Postgres db).
Lets say I have empty database and I have these two lines of code:
parentRepository.save(parent);
parentRepository.save(parent);
After these two lines I will have single row in table parent in database, but every of his children is in table child twice.
Given that you are using #GeneratedValue, the child entity id will be generatet when you call sava for the first time.
If you check the save method documentation, you will see that it does return a new instance of the object you saved.
This new instance contains the parent entity with the child entities with the ids generated by spring on those child entities.
Another important change you need to do is to use wrapper types instead of primitives in the attributes annotated with #Id. Wrappers can contain a null value, this is important for spring to know if the entity is a new entity or is an entity to be updated. Check spring-data-jpa docs for more information.
The problem with your code is first that you are using primitive type attributes annotated with #Id (change it to wrapper types) and the second is that you are supposed to use the returned object from the save method for further operations.
I'm trying to model a business entity, where said business can have several parent businesses and several child businesses. I'm not sure which relationships are suited, or even what mapping is appropriate. I'm familiar with SQL but new to ORM in Java.
My thinking is that a business can have many or none children, and a business can have many or none parents. Therefore I've tried setting both as OneToMany but also as OneToMany, both resulting in this error: Illegal use of mappedBy on both sides of the relationship.
The implementation:
#Entity
public class Business{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy = "parentOrgs")
private Collection<Business> chlidOrgs;
#OneToMany(mappedBy = "chlidOrgs")
private Collection<Business> parentOrgs;
// --- Getters and setters below ---
What am I not understanding here? Any and all help much appreciated.
Your current mapping is syntactically incorrect, because only one side of the relationship can be owning side. Owning side is the field defined by value of mappedBy attribute. More detailed explanation can be found from here.
Also removing mappedBy from the one side does not solve the problem, because counterpart of OneToMany must be ManyToOne. Removing it from the both sides leaves us with two unirectional associations, which is also not what is needed.
Because each Business can have multiple parents and it seems to be preferred to be able to navigate directly to the childrens as well, solution is to use bidirectional ManyToMany:
#Entity
public class Business {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany(mappedBy = "parents")
private Collection<Business> childrens;
#ManyToMany
private Collection<Business> parents;
}
From database point of view this means following tables:
Business(id)
Business_Business(childrens_id, parents_id)
When necessary, name of the join table and columns can be controlled via JoinTable.
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);
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 have two entities
#Entity
#Table(name="parent")
public class Parent {
#Id
String uuid;
#ElementCollection(fetch=FetchType.EAGER)
#CollectionTable(
name="child",
joinColumns=#JoinColumn(name="parent_uuid", insertable=false, updatable=false)
)
#Column(name="uuid")
private Set<String> childrenUuids = new HashSet<String>();
}
#Entity
#Table(name="child")
public class Child {
#Id
String uuid;
#Column(name="parent_uuid")
String parentUuid;
}
now when I persist Parent, the children in childrenUuids are automatically persisted because the ManyToOne relationship. I want to prevent all operations to Parent(e.g. persist, remove ...) being cascaded to Child, is it possible for JPA? I have been researching for a few days, but could not find the answer. thank you.
You should use #OneToMany instead of #ElementCollection. A #OneToMany does not cascade by default. A #ElementCollection always cascades, as far as I know, which kind of makes sense, since "#ElementCollection defines a collection of instances of a basic type or embeddable class", and basic types/embeddables are considered an integral part of their parent.
#ElementCollection does always cascade. I finally resolve this by implementing my solution for #ElementCollection. I still use JPA annotations, instead, I add #Transient above #ElementCollection to make JPA ignore it. then I put my implementation as a JPA post-load listener to each entity, which will fill up each collection after the entity is loaded.