Suppose we have bidirectional OneToOne relationship.
There are User and Address entity. User has many Addresses.
CREATE SEQUENCE IF NOT EXISTS hibernate_sequence;
CREATE TABLE users (
id BIGINT PRIMARY KEY
);
CREATE TABLE addresses (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL UNIQUE CONSTRAINT fk_addresses_user_id REFERENCES users(id) ON DELETE CASCADE,
);
and
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
//mappings
private Address address;
}
@Table(name = "addresses")
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
//mapings
private User user;
}
I'm using OneToOne as an example but the question is about relations in general so the right approach is valid for ManyToMany as well.
TL;DR - the question is about using mappedBy<->relation owner<->cascades<->managing other side in setters in general
User or Address) / (Owner or Inverse)?
Address should have mappedByPost (User in my example) have mappedBy to PostDetails (Address in my example)User owns the relation, and therefore Address should have mappedBy (CustomerRecord in JavaDoc)Phone (Person in my example) has mappedBy to PhoneDetails (Address)User or Address) / (mappedBy or inverse-side) the @OneToOne(cascade = {CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH}) should be placed? public void addAddress(Address address) {
if (address != null) {
address.addUser(this);
}
this.addresses.add(address);
}
public void removeAddress(Address address) {
if (address != null) {
address.removeUser(this);
}
this.addresses.remove(address);
}
public Set<Address> getAddresses() {
return Collections.unmodifiableSet(this.addresses);
}
the (User or Address) / (mappedBy or inverse-side)?
User in my example, manages the relation)Moreover - look at theese two links:
Thank you in advance
PS. Previous version of my question was using OneToMany as an example, but because inverse - ManyToOne does not have mapped by I changed it to OneToOne which better shows the problem
In a relational database system, there are three types of table relationships:
So, a one-to-many table relationship looks like this:

Note that the relationship is based on the post_id Foreign Key column in the child post_comment table.
So, the Foreign Key column is the single source of truth when it comes to managing a one-to-many table relationship.
Now, let's take a bidirectional JPA entity relationship that maps the one-to-many table relationship we saw previously:

If you take a look at the diagram above, you can see that there are two ways to manage this relationship.
In the Post entity, you have the comments collection:
@OneToMany(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();
And, in the PostComment, the post association is mapped as follows:
@ManyToOne(
fetch = FetchType.LAZY
)
@JoinColumn(name = "post_id")
private Post post;
So, you have two sides that can change the entity association:
comments child collection, a new post_comment row should be associated with the parent post entity via its post_id column.post property of the PostComment entity, the post_id column should be updated as well.Because there are two ways to represent the Foreign Key column, you must define which is the source of truth when it comes to translating the association state change into its equivalent Foreign Key column value modification.
The mappedBy attribute tells that the @ManyToOne side is in charge of managing the Foreign Key column, and the collection is used only to fetch the child entities and to cascade parent entity state changes to children (e.g., removing the parent should also remove the child entities).
It's called the inverse side because it references the child entity property that manages this table relationship.
Now, even if you defined the mappedBy attribute and the child-side @ManyToOne association manages the Foreign Key column, you still need to synchronize both sides of the bidirectional association.
The best way to do that is to add these two utility methods:
public void addComment(PostComment comment) {
comments.add(comment);
comment.setPost(this);
}
public void removeComment(PostComment comment) {
comments.remove(comment);
comment.setPost(null);
}
The addComment and removeComment methods ensure that both sides are synchronized. So, if we add a child entity, the child entity needs to point to the parent and the parent entity should have the child contained in the child collection.
This is what I have learnt so far.
If I'm mistaken to any of the points - please let me know in comments, and I edit the answer.
`According to this answer
Basically, the owner is address, as it's the owner who holds the reference.
So the "owner" of relation in database is entity with foreign key - Address in case of OneToOne/OneToMany.
Moreover - it seems that in the same time "owner" of the database relation is the Child in Hibernate relation - but I need confirmation for this
According to this article
The Post entity is the parent, while the PostDetails is the child association because the Foreign Key is located in the post_details database table
So User is the Parent and Address is the Child here (because Address holds foreign key)
According to this answer
The mappedBy attribute marks the side of a bidirectional association which does not own the association. Usually, that's the side which does not have the Foreign Key.
So mappedBy should be placed in User, because foreign key is in Address.
In seems that in @OneToOne mappedBy should be always placed in Parent (entity not holding the foreign key)
It seems that JoinColumn in OneToOne must be always placed in Child (since it holds foreign key)
According to this answer
cascading entity state transitions only makes sense from parents to child entities.
So cascades in OneToOne should be always done in Parent, so - according to previous links - User in this example
I'm not sure but it seems that utility setter should be placed in User.
Is it always placed in the Parent?
CREATE SEQUENCE IF NOT EXISTS hibernate_sequence;
CREATE TABLE users (
id BIGINT PRIMARY KEY
);
CREATE TABLE addresses (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL UNIQUE CONSTRAINT fk_addresses_user_id REFERENCES users(id) ON DELETE CASCADE,
);
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false, orphanRemoval = true)
private Address address;
public void setAddress(Address address) {
if (address != null) {
address.setUser(this);
}
this.address = address;
}
}
@Table(name = "addresses")
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
}
It seems to be the same as with OneToOne, so User is the Parent and Address is the Child (because it has foreign key), but I'm not 100% sure...
In this article mappedBy is placed in Parent (but I'm not sure if this is the rule of thumb though)
It seems that JoinColumn in OneToMany must be always placed in Child (since it holds foreign key)
In above article cascades are also placed in Parent (but I'm not sure if this is the rule of thumb as well)
In above article utility methods are also placed in Parent (but I'm not sure if this is the rule of thumb as well)
CREATE SEQUENCE IF NOT EXISTS hibernate_sequence;
CREATE TABLE users (
id BIGINT PRIMARY KEY
);
CREATE TABLE addresses (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL CONSTRAINT fk_addresses_user_id REFERENCES users(id) ON DELETE CASCADE,
);
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
private List<Address> addresses = new ArrayList<>();
public void addAddress(Address address) {
if (address != null) {
address.setUser(this);
}
this.addresses.add(address);
}
public void removeAddress(Address address) {
this.addresses.remove(address);
if (address != null) {
address.setUser(null);
}
}
public Set<Address> getAddresses() {
return Collections.unmodifiableSet(this.addresses);
}
}
@Table(name = "addresses")
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
}
According to this article
A typical many-to-many database association includes two parent tables
So both User and Address are Parents
In the same article Vlad wrote:
The mappedBy attribute of the posts association in the Tag entity marks that, in this bidirectional relationship, the Post entity own the association
Therefore here is the eception, because we don't have one Parent.
It is not clear to me what Vlad means exactly by "own", but if User "owns" the relation, mappedBy must be placed in Address,
It seems that JoinTable must be always placed in entity that "owns" the relation, so User in this case.
Am I right?
According to the same article cascades should be always placed in "owner" defined above, so User in this case.
Also important thing to notice is that we can not use REMOVE cascade
It seems that add/remove utility methods should be placed in User.
Is this the rule of thumb, that utility methods should be alway placed in entity that "owns" the relation?
CREATE SEQUENCE IF NOT EXISTS hibernate_sequence;
CREATE TABLE users (
id BIGINT PRIMARY KEY
);
CREATE TABLE addresses (
id BIGINT PRIMARY KEY,
);
CREATE TABLE users_addresses (
user_id BIGINT NOT NULL CONSTRAINT fk_users_addresses_user_id REFERENCES users(id) ON DELETE CASCADE,
address_id BIGINT NOT NULL CONSTRAINT fk_users_addresses_address_id REFERENCES addresses(id) ON DELETE CASCADE,
PRIMARY KEY(user_id,address_id)
);
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@ManyToMany(cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
@JoinTable(name = "post_tag",
joinColumns = @JoinColumn(name = "post_id"),
inverseJoinColumns = @JoinColumn(name = "tag_id")
)
private List<Address> addresses = new ArrayList<>();
public void addAddress(Address address) {
addresses.add(address);
address.getUsers().add(this);
}
public void removeAddress(Address address) {
addresses.remove(address);
address.getUsers().remove(this);
}
}
@Table(name = "addresses")
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@ManyToMany(mappedBy = "addresses")
private List<User> users = new ArrayList<>();
}
I think this table sums it up: https://i.ibb.co/zNjZ3md/JPA-relations.png
The last thing I don't unserstand is:
mappedBy in Author ("owner") instead of Book (especially that it throws exception in my code)mappedBy is on CustomerRecord (child) not on Customer (but here maybe foreign key is located in Customer, so although counter intuitive - it is correct)If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With