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.
Related
I know that lazy loading can't be done if you have a two-way relationship in a one-to-one relationship.
So I read about How can I make a JPA OneToOne relation lazy.
The article says that you can't create a proxy in a one-to-one relationship, but I can't quite understand it.
I think There is no difference between a many-to-one relationship and a one-to-one relationship. He says that in a many-to-one relationship you can create a proxy because you can get the value from FK.
However, one-to-one relationships can also get values with FK, there is no explanation as to why exactly one-to-one relationships can't only get values.
Can someone please explain clearly?
// one to one
class Address {
..
#OnetoOne(fetch = LAZY)
#JoinColumn(~)
private Order order;
}
class Order {
..
#OnetoOne(mappedBy = order, fetch = LAZY)
private Address address; // LAZY not wokring
}
// ManyToOne
public class Member {
#ManyToOne(fetch = LAZY)
#JoinColmn(~)
public Team team; // LAZY working!
}
I think these two are different. I know the PK values equally, so why doesn't only the one-to-one relationship do lazy loading?
Address and Member are the same. They own the column representing the association. Let's say, for example, the column team_id in the table Member and the column order_id in the table Address.
When Hibernate ORM reads a row from the table Member, by checking the value of the team_id column, it knows if that value is null or if there is an association. Therefore, when the entity is created it can decide if the value mapping the association (Mapping#team)is null or a proxy. In this case lazy fetching is possible.
The table Order doesn't have a column representing the association, that's because the association is mapped by the column order_id in Address. When Hibernate ORM needs to convert a row in the table Order to an entity, it also needs to check the table Address to see if an association exists. And depending on the result, it will set the value Order#address to null or the associated address. In this case, it cannot know if the value of the association is null or not without checking the table Address: lazy fetching is not possible.
Note that it might still be possible to lazy fetch the association when the mapping states that the association is never null: #OneToOne(mappedBy="order", optional=false).
Basically, the problem is that if a value can be mapped to null, Hibernate ORM needs to check first if this value is actually null because, otherwise, code like if (order.address == null) { is not going to work if a proxy is created.
Empty *-to-many associations are different in this regard because Hibernate ORM maps them with an empty collection (and never with null) and this means that it can always return a proxy.
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?
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 think I misunderstood the meaning of cascading in the context of a #ManyToOne relationship.
The case:
public class User {
#OneToMany(fetch = FetchType.EAGER)
protected Set<Address> userAddresses;
}
public class Address {
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
protected User addressOwner;
}
What is the meaning of the cascade = CascadeType.ALL? For example, if I delete a certain address from the database, how does the fact that I added the cascade = CascadeType.ALL affect my data (the User, I guess)?
The meaning of CascadeType.ALL is that the persistence will propagate (cascade) all EntityManager operations (PERSIST, REMOVE, REFRESH, MERGE, DETACH) to the relating entities.
It seems in your case to be a bad idea, as removing an Address would lead to removing the related User. As a user can have multiple addresses, the other addresses would become orphans. However the inverse case (annotating the User) would make sense - if an address belongs to a single user only, it is safe to propagate the removal of all addresses belonging to a user if this user is deleted.
BTW: you may want to add a mappedBy="addressOwner" attribute to your User to signal to the persistence provider that the join column should be in the ADDRESS table.
You shouldn't use CascadeType.ALL on #ManyToOne since entity state transitions should propagate from parent entities to child ones, not the other way around.
The #ManyToOne is on the child side of the association as it maps the underlying Foreign Key column.
Therefore, you should move the CascadeType.ALL from the #ManyToOne association to the #OneToMany side, which should also use the mappedBy attribute since it's the most efficient one-to-many table relationship mapping.
See here for an example from the OpenJPA docs. CascadeType.ALL means it will do all actions.
Quote:
CascadeType.PERSIST: When persisting an entity, also persist the entities held in its fields. We suggest a liberal application of this cascade rule, because if the EntityManager finds a field that references a new entity during the flush, and the field does not use CascadeType.PERSIST, it is an error.
CascadeType.REMOVE: When deleting an entity, it also deletes the entities held in this field.
CascadeType.REFRESH: When refreshing an entity, also refresh the entities held in this field.
CascadeType.MERGE: When merging entity state, also merge the entities held in this field.
Sebastian
From the EJB3.0 Specification:
Use of the cascade annotation element may be used to propagate the
effect of an operation to associated entities. The cascade
functionality is most typically used in parent-child relationships.
If X is a managed entity, the remove operation causes it to become
removed. The remove operation is cascaded to entities referenced by X,
if the relationships from X to these other entities is annotated with
the cascade=REMOVE or cascade=ALL annotation element value.
So in a nutshell, entity relationships defined with CascadeType.All will ensure that all persistence events such as persist, refresh, merge and remove that occur on the parent, will be passed to the child. Defining other CascadeType options provides the developer with a more granular level of control over how the entity association handles persistence.
For example if I had an object Book that contained a List of pages and I add a page object within this list. If the #OneToMany annotation defining the association between Book and Page is marked as CascadeType.All, persisting the Book would result in the Page also being persisted to the database.
In JPA 2.0 if you want to delete an address if you removed it from a User entity you can add orphanRemoval=true (instead of CascadeType.REMOVE) to your #OneToMany.
More explanation between orphanRemoval=true and CascadeType.REMOVE is here.
If you just want to delete the address assigned to the user and not to affect on User entity class you should try something like that:
#Entity
public class User {
#OneToMany(mappedBy = "addressOwner", cascade = CascadeType.ALL)
protected Set<Address> userAddresses = new HashSet<>();
}
#Entity
public class Addresses {
#ManyToOne(cascade = CascadeType.REFRESH) #JoinColumn(name = "user_id")
protected User addressOwner;
}
This way you dont need to worry about using fetch in annotations. But remember when deleting the User you will also delete connected address to user object.
What exactly is the difference in the following two declarations
B is the owning side
#Entity
class A {
#Id int id;
#OneToOne
B b;
}
#Entity
class B {
#Id int id;
#OneToOne(mappedBy="b")
A a;
}
A is the owning side
#Entity
class A {
#Id int id;
#OneToOne(mappedBy="a")
B b;
}
#Entity
class B {
#Id int id;
#OneToOne
A a;
}
Thinking of this in "normal SQL" i think it is the same as having two tables each having the other table's foreign key. What i don't understand though is what is the effect of specifying which entity is the owning side i.e using the 'mappedBy' property. What does this actually achieve as i don't believe there is an equivalent in normal SQL.
The JPA 2.0 specification, section 2.9, writes:
Relationships may be bidirectional or unidirectional. A bidirectional relationship has both an owning side and an inverse (non-owning) side. A unidirectional relationship has only an owning side. The owning side of a relationship determines the updates to the relationship in the database, as described in section 3.2.4.
The following rules apply to bidirectional relationships:
The inverse side of a bidirectional relationship must refer to its owning side by use of the mappedBy element of the OneToOne, OneToMany, or ManyToMany annotation. The mappedBy element designates the property or field in the entity that is the owner of the relationship.
The many side of one-to-many / many-to-one bidirectional relationships must be the owning side, hence the mappedBy element cannot be specified on the ManyToOne annotation.
For one-to-one bidirectional relationships, the owning side corresponds to the side that contains the corresponding foreign key.
For many-to-many bidirectional relationships either side may be the owning side.
The relevant parts of section 3.2.4 are:
The state of persistent entities is synchronized to the database at transaction commit. This synchronization involving writing to the database any updates to persistent entities and their relationships as specified above.
and
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 that the semantics of the relationships are adhered to.
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.
The owning side is the side that JPA considers to know is the association exists or not. Suppose you go with your first example. The owning side is the side where there is no mappedBy attribute. The owning side is thus A, and not B.
This means that if you have an A and a B in database, and you do
A a = em.find(A.class, aId);
B b = em.find(B.class, bId);
a.setB(b);
JPA will save the association (i.e. it will store the ID of B in the join column of table A).
But if you do
A a = em.find(A.class, aId);
B b = em.find(B.class, bId);
b.setA(a);
nothing will be changed in database, because you modified the inverse side and forgot to modify the owning side.
As other have pointed out, you are wrong about which side is the owning side in your examples.
With owning side we mean owning the relationship from an OO perspecitve, in practise that quite often ends up being the opposite of how it is or will be generated in the db if one uses a rdbm as persistence provider.
In normal circumstances the OO model makes it quite clear which sides is the owning side.
For example an Order has OrderLines. If we delete an Order all Orderlines should be deleted. If we delete an OrderLine the Order possibly still has a right to existence.
Hence, the Order is the owning side.
For a more concrete and excellent example, on the effects of which side is the owning side, I refer to #JB Nizet answer.
According to section 2.9 of the JPA 2.0 spec:
For one-to-one bidirectional relationships, the owning side
corresponds to the side that contains the corresponding foreign key.
But in the same section we also have:
In addition, this specification also requires support for the
following alternative mapping strategies: [..] The mapping of
unidirectional and bidirectional one-to-one relationships,
bidirectional many-to-one/one-to-many relationships, and
unidirectional many-to-one relationships by means of join table
mappings.
A bit futher down in the same section it continues with:
Additional mapping annotations (e.g., column and table mapping
annotations) may be speci- fied to override or further refine the
default mappings and mapping strategies described in Section 2.10.
Some implementations make use of that to allow the FK of a birectional OneToOne to be in the target table.
To read some about some strategies to solve that scenario, see: An almost good explaination
I haven't checked but I do hope and believe that 2.1 will remove the first quote. Since the actual database structure should put as little limit as possible on how we can model data as entities.
In the first example the A table is going to have 2 columns id and b_id, the B table is going to have one column, id. This makes A the owning side.
In the second example B is the owning side. B has two columns, id and a_id. A is going to have one column, id.
And that is the difference :-)