So far my understand was that cascading only makes sense from the parent to the child. Now I'm wondering: does this also applies to OneToOne relationships?
I'm asking because I found in our code many (unidirectional) OneToOne relationships with cascading from child to parent. I tested it with "persist" and it seems to work - meaning the transient child is persisted together with the transient parent. While looking in the literature I found examples of such a cascading strategy. For instance on baeldung:
// CHILD
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private Long id;
//...
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "address_id", referencedColumnName = "id")
private Address address;
}
//PARENT
@Entity
@Table(name = "address")
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private Long id;
//...
@OneToOne(mappedBy = "address")
private User user;
}
In some other articles, the cascading is done the other way around. For instance on Vlad Mihalcea's blog.:
//CHILD
@Entity(name = "PostDetails")
@Table(name = "post_details")
public class PostDetails {
@Id
@GeneratedValue
private Long id;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "post_id")
private Post post;
}
//PARENT
@Entity(name = "Post")
@Table(name = "post")
public class Post {
@Id
@GeneratedValue
private Long id;
@OneToOne(mappedBy = "post", cascade = CascadeType.ALL,
fetch = FetchType.LAZY, optional = false)
private PostDetails details;
}
So at the end are they both two valid options? I'm quite confused.
Firstly, I am not a fan of
CASCADE. Why? Simply because if you don't know what is going on, why use a thing?I can't answer your question because it depends on what you think is correct. But I can help shed some light on the issues.
You need to understand the "owning entity". From the Javadocs:
In the first case, the
Useris the owning entity. In JPA, only the owning side will persist relationships. In this case only if theAddressfield is set in theUserentity will JPA try to persist the relationship.A persist operation would be:
Which results in:
No problem there. But what if the the
Addressis already persisted?Results in:
Using
Cascadehas problems because it's tring to insert both but the current address already exists. You can work around it:Which results in this lovely bit of SQL:
Or perhaps you could delete the existing address record first if it exists, but then you have to check first and so on. Not a bad solution. Be sure to do it in a transaction.
Or you could avoid using cascade altogether. You have to save both the
AddressandUseryourself, or use an already saved instance, which also means you have to check first, but at least you "know" what you are doing.Results in:
So, which side should you put the
Cascadeon? If you are going to insist on using it and making your life that much more complicated, it should go on the owning side of the relationship, in this case theUser.Or, just leave it out and be happy.