I have tow classes:
Parent
Child
In the database Child table has a column ParentId -> typical One (Parent) -> Many (Children) relation
Now I create two entities and they
public class Parent
{
#OneToMany(mappedBy="Parent", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
public Set<Child> getChildern()
{
...
}
}
public class Child
{
#ManyToOne(fetch = FetchType.EAGER, cascade=CascadeType.ALL)
#JoinColumn(name="ParentId")
public Parent getParent()
{ ... }
}
Now I have two scenarios:
Parent gets deleted -> what should happen?
Child gets deleted -> what should happen?
Bonus questions:
Do I always need to create both parts of the key OneToMany and ManyToOne or can i just have ManyToOne and dont care in the parent where I have children?
What could cause a hibernate to give me a message foreign key constraint violation for a parent which has no children?
First of all I'm surprised this code works at all. IMO mappedBy="Parent" should actually be mappedBy="parent" (note the lower-case 'p') because the parent property of the Child class is called parent and not Parent.
Second, I suggest you place the annotations on the properties rather than on the accessor methods. I find it makes the whole code
more readable
easier to maintain because getters/setters can then be added behind the scenes by Lombok
Answers to your questions depend on what exactly you mean by "get deleted". I assume you mean "deleted through persistence manager".
BUT just in case you expect/want that a child is removed by the JPA provider if you do parent.getChildren().remove(x) then you need to set orphanRemoval = "true" on OneToMany.
Question 1
Parent and all children are deleted. That's the common case.
Question 2
Parent and all children are deleted. That's a rather odd use case. Usually cascade delete is only applied on the one-to-many relationship.
Bonus 1
All relationships in Java and JPA are unidirectional, in that if a
source object references a target object there is no guarantee that
the target object also has a relationship to the source object.
from the excellent Java Persistence wiki book.
Bonus 2
Dunno. Is the ConstraintViolationException coming from the underlying data base? Or put differently, how does the DDL for the two tables look like? Was it generated by Hibernate?
Related
I am trying to use Hibernate to store family tree information. From what I have seen in the documentation, in order to connect two or more entities, they have to be from different classes. So, in order to create relationships between husband and wife, I would need to have two classes respectively. I think this is pointless because both classes would be identical (keep in mind that the tree can be quite large so I would have a lot of duplicate classes that way).
Is there a way to have a single class, for example Person and do the connections just from that class?
Also, if there is not way to achieve that, how would I connect siblings, for example
(p:Sibling)-[:SIBLING_OF]->(k:Sibling)
when they will both be from same class Sibling?
You can create relationships with entities of the same class the same way you create relationships with entities of different classes.
You can find an example of the mapping on the Hibernate OGM project sources:
https://github.com/hibernate/hibernate-ogm/blob/5.2.0.Alpha1/core/src/test/java/org/hibernate/ogm/backendtck/associations/recursive/TreeNode.java
and the realtive testcase:
https://github.com/hibernate/hibernate-ogm/blob/5.2.0.Alpha1/core/src/test/java/org/hibernate/ogm/backendtck/associations/recursive/RecursiveAssociationsTest.java
The tests map a tree structure with nodes having a parent node and many children, the mapping of the entity looks like like this:
#Entity
public class TreeNode {
#Id
private String name;
#ManyToOne
private TreeNode parent;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "parent",
cascade = CascadeType.ALL, orphanRemoval = true)
private List<TreeNode> children = new ArrayList<TreeNode>( 3 );
...
}
NOTE:
Based on your needs, you can create the association using native queries but I wouldn't recommend it. Hibernate OGM becomes unaware of the relationship and problems might occur.
You can use CYPHER query for creating relationship for same class entities
as follow
Match(u:sibling{name:'abc'}),Match(p:sibling{name:'xyz'})
CREATE (u)-[:SIBLING_OF]-(p)
executing CYPHER query can be found here
I have 2 classes called PurchaseList.java and PurchaseListItems.java
I have to map PurchaseList in PurchaseListItems
PurchaseList.java
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name="pl_id",referencedColumnName="id")
private List<PurchaseListItems> purchaseListItems;
PurchaseListItems.java
#ManyToOne
#JoinColumn(name="pl_id")
private PurchaseList purchaseListId;
Everything is fine but i am getting null in pl_id. Please tell where i am wrong
For some reason mapped by didn't work for me with postgres sql and Hibernate4
Below mapping worked
PurchaseList.java
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name="pl_id",nullable=false)
private List<PurchaseListItems> purchaseListItems;
PurchaseListItems.java
#ManyToOne
#JoinColumn(name="pl_id", nullable=false,insertable=false,updatable=false )
private PurchaseList purchaseListId;
Note: you have to use the Identity or Explicitly mention the Sequence for id columns for postgres.
#GeneratedValue(strategy=GenerationType.IDENTITY)
Your mapping actually defines two independent unidirectional relations. What you want is one bidirectional relation.The following code will establish the bidirectional relation
#OneToMany(cascade = CascadeType.ALL, mappedBy = "purchaseListId")
#JoinColumn(name="pl_id",referencedColumnName="id")
private List<PurchaseListItems> purchaseListItems;
The mappedBy attribute is necessary since there is no way for the provider to automatically determine that the specified relations actually form a single relation. One could use the Java type of the instance member but then what if you have multiple members of the same type. And there are many scenarios where you have two single relations. Example:
OneToMany: User -> ForumThread (the threads created by the user)
ManyToOne: ForumThread -> User (the user who closed the thread. obviously not necessarily the one who started the thread)
These are two independent relations and must be treated as such. You would be quite surprised if your persistence provide just made a bidirectional relation out of that just because the types and multiplicity matched.
Also note that bidirectional relations are not automatically managed by any JPA provider, meaning that the inverse side is not automatically updated/set in your object model and thus not in the db. You have to do that yourself. By the way, in all my projects bidirectional relationships were a pain in the ass and I think it is advisable to avoid them.
The #JoinColumn annotation belongs on the #ManyToOne side of the relationship - but not on the #OneToMany side - remove it from the #OneToMany side.
Cascade is used to cascade DELETE/READ/UPDATE operations..., but it does not automatically populate the ID column on the "child" side of a foreign key. In fact, it doesn't populate the java references to objects on either side of the FK relationship. You need to manually setup relationship data on both sides of bidirectional relationships:
myPurchaseListItem.setPurchaseList(myPurchaseList);
myPurchaseList.setPurchaseListItem(myPurchaseListItem);
From the JPA 2 spec:
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 the case of unidirectional one-to-one and one-to-many relationships, it is the developer’s responsibility to insure (sic) that the semantics of the relationships are adhered to.[29]
It is particularly important to ensure that changes to the inverse side of a relationship result in appropriate updates on the owning side, so as to ensure the changes are not lost when they are synchronized to the database.
for(PurchaseListItems item:purchaseListItemsList)
item.purchaseListId(PurchaseList);
This is what I missed when i am creating an object.
Thnaks for your answers
The jpa specification looks good, but verify you have given valid parent to child relationship in the database. If there is not a reference then it will return null.
try this
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "purchaseListId")
Check if you have populated purchaseListId with valid value (a created PurchaseList instance) when you create a PurchaseListItems value.
It's better to use mappedBy as below code to let many-side to maintian the relationship.
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "purchaseListId")
#JoinColumn(name="pl_id",referencedColumnName="id")
private List<PurchaseListItems> purchaseListItems;
I have a bidirectional many to one relationship. When I retrieve the parent object from the table, all the child objects should get retrieved but only the first one is getting retrieved
The parent looks like
#Entity
public class xyz{
#OneToMany(mappedBy="xyz",cascade=CascadeType.PERSIST,fetch=FetchType.EAGER)
private Set<zyx> zyxDO;}
The child class looks like
public class zyx{
#ManyToOne
#JoinColumn(name="id")
private xyz xyzDO;
}
Is there any annotation where I can retrieve all the rows of the underlying database
Maybe it is a typo, but the value of mappedBy should be the name of the attribute in the owning entity that points back to the inverse entity, in this case xyzDO.
And maybe because you are using a Set instead of a Collection could cause that only one child is retrieved (if they are identical).
What do you mean by retrieve all the rows of the underlying database? You retrieve only the children which parent's key set. Note also, that fetch eager can easily cause great performance issues, because children will be fetched always, even if you don't need them.
Ensure you set both sides of the relationships when adding/setting the relationship.
Supposing I have two entities a parent object and a child. I want the child to have a relationship specified to the parent entity and a reference, but I don't want the parent to have to know about it. The reason for this is there are numerous such relationships and explicitly specifying the relationship for all these children will mean continually updating and polluting the parent objects. However I still want deletes of the parent object to cascade to the child. As an additional constraint the child object only ever has the reference to the parent entity set by setting an attribute that references the parent's ID. Supposing I have something like:
#Entity
class Child {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Column(nullable=false)
private Long parentId;
#ManyToOne(fetch=FetchType.LAZY, optional=false)
#JoinColumn(name="parentId", insertable=false, updatable=false, nullable=false, unique=false)
private Parent parentEntity;
// etc...
}
#Entity
class Parent {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
// etc...
}
Then is there any way that deletes of the parent object can be cascaded to the child without adding an additional relationship to the parent entity?
A solution that uses only JPA annotations would be preferred, but any help would be appreciated.
No it's not possible.
Cascades (of any kind) traverse relationships. If you don't tell JPA about the relationship from A to B (even if there is one from B to A) then it can't automatically traverse it.
On a side note: only specifying the parent ID rather than the parent entity is going to make it even more difficult. Either Hibernate/JPA isn't the right tool for what you're doing or you need to embrace how it works, meaning specifying and maintaining object relationships.
Adding the parentId instead of the parent directly makes your life way harder. Hibernate is only saving an id in the database. So whats your point of doing it yourself?
Back to your problem. You have to make the relationship bidirectional and add cascade=CascadeType.ALL to your children attribute which itself is mappedBy="parent". It's small, it's easy, it's fast.
I have two entities called User and UserProfile in my data model. Here is how they are mapped.
Code from User Entity:
#OneToOne(cascade=CascadeType.ALL)
#PrimaryKeyJoinColumn
public UserProfile getUserProfile(){
return this.userProfile;
}
public void setUserProfile(UserProfile userProfile){
this.userProfile=userProfile;
}
Code from UserProfile Entity:
#OneToOne(mappedBy="userProfile",cascade=CascadeType.ALL)
public User getUser(){
return this.user;
}
public void setUser(User user){
this.user=user;
}
As you see, I have a CascadeType.ALL for the user attribute in UserProfile. But when I try deleting the UserProfile entity, the corresponding User entity still stays. (When I try deleting the User entity, the corresponding UserProfile entity gets deleted.)
Here is my question:-
Do cascades hold only when I specify them on the entity owning the relationship?
Your question is wrong in and of itself, which is where all the confusion stems from. Arthur did a good job with his answer but it's clear from the comments the the confusion still remains so let me take a stab at it here.
Do cascades hold only when I specify
them on the entity owning the
relationship?
"cascade" is an attribute you specify on one (or possibly both in case of bi-directional) end of a relationship. It determines what actions performed on that end will be propagated to the other end. There are many different types of those actions defined in JPA and even more defined in Hibernate extensions. This distinction is important - you should only talk about specific behavior being propagated and not "cascade" in general.
PERSIST, MERGE, REFRESH propagate normally (from the end they were declared on to the other).
REMOVE, however, is tricky because it can mean two different things. If you have a relationship between A and B and you're trying to remove A, you can either remove B on the other end OR you can remove the association but leave B intact.
Hibernate makes a clear distinction between the two - you can declare REMOVE (DELETE) and DELETE_ORPHAN cascade types separately; JPA spec does not. Note that DELETE_ORPHAN is not supported to single-valued relationships (OneToOne / ManyToOne).
Thus, propagation of REMOVE (by itself or when it's part of ALL) depends on whether relationship has a clear owner (uni-directional always does; bi-directional does if it's mapped using mappedBy and does not if it's mapped via join table) in which case it's propagated from owner to owned OR no owner in which case it's propagated in either direction but without DELETE_ORPHAN semantics unless it was explicitly specified. Typical example of the latter is bi-directional many-to-many.
As said
When i try deleting the UserProfile entity, the corresponding User entity still stays
Maybe when you try to remove a UserProfile you get an integrity constraint violation from the database - do you use MyISAM engine in MySQL ?
But as you does not says nothing about it. Maybe your UserProfile entity does not have a reference to a User entity.
As said in JPA specification
remove operation is cascaded to entities referenced by X, if the relationship from X to these other entities is annotated with the cascade=REMOVE or cascade=ALL annotation element value
Something like
UserProfile up = entityManager.find(UserProfile.class, id);
entityManager.close();
// Notice User is null outside a persistence context
// So user will be not removed from the database because UserProfile does not have a reference to it
up.setUser(null);
entityManager.getTransaction().begin();
entityManager.remove(up);
entityManager.getTransaction().commit();
Or you have something like
entityManager.getTransaction().begin();
UserProfile up = entityManager.find(UserProfile.class, id);
// throws UPDATE USER_PROFILE SET USER_ID = NULL
up.setUser(null);
// up.getUser() is null
// So user is not removed
entityManager.remove(up);
entityManager.getTransaction().commit();
In response to ChhsPly's comment:
In Java Persistence with Hibernate book, you see the following
The cascade attribute is directional: It applies to only one end of the association.
I think it would be better as
It applies to only one end of the association per operation
So you can put cascade attribute in both sides at the same time, even in a bidirectional relationship. So ChssPly is right.
mappdeBy attribute sets up the bidirectional relationship. mappedBy attribute designated the Address entity as the inverse side of the relationship. This means that the Customer entity is the owning side of the relationship.
ChssPly is right when he says mappedBy has nothing to do with cascading
That is correct when you have a bi-directional relationship the owner dictates the cascade rules since it is the "owner". The "owned" entity essentially follows orders, it can't give the orders -- so to speak.
With JPA 2.x , if you want a cascade remove then use orphanRemoval attribute :
#OneToMany(orphanRemoval=true)
check documentation here for more info.