557

What is the difference between:

@Entity
public class Company {

    @OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY)
    @JoinColumn(name = "companyIdRef", referencedColumnName = "companyId")
    private List<Branch> branches;
    ...
}

and

@Entity
public class Company {

    @OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY, 
    mappedBy = "companyIdRef")
    private List<Branch> branches;
    ...
}
Mike
  • 17,033
  • 22
  • 85
  • 113
  • 2
    Also see [What is the owning side in an ORM mapping](https://stackoverflow.com/questions/2749689/what-is-the-owning-side-in-an-orm-mapping) question for a really good explanation of the issues involved. – dirkt Nov 15 '18 at 11:57

8 Answers8

582

The annotation @JoinColumn indicates that this entity is the owner of the relationship (that is: the corresponding table has a column with a foreign key to the referenced table), whereas the attribute mappedBy indicates that the entity in this side is the inverse of the relationship, and the owner resides in the "other" entity. This also means that you can access the other table from the class which you've annotated with "mappedBy" (fully bidirectional relationship).

In particular, for the code in the question the correct annotations would look like this:

@Entity
public class Company {
    @OneToMany(mappedBy = "company",
               orphanRemoval = true,
               fetch = FetchType.LAZY,
               cascade = CascadeType.ALL)
    private List<Branch> branches;
}

@Entity
public class Branch {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "companyId")
    private Company company;
}
Óscar López
  • 215,818
  • 33
  • 288
  • 367
  • 4
    in both cases Branch has field with Company id. – Mike Aug 13 '12 at 16:25
  • 3
    Company table doesn't have a column with a foreign key to the referenced table - Branch has ref to Company.. why are you saying "the corresponding table has a column with a foreign key to the referenced table" ? Could you explain some more pls. – Mike Aug 13 '12 at 16:33
  • 13
    @MykhayloAdamovych I updated my answer with sample code. Notice that it's a mistake to use `@JoinColumn` in `Company` – Óscar López Aug 13 '12 at 16:33
  • so there is no legitimate case where JoinColumn could be used in a couple with OneToMany, right? – Mike Aug 13 '12 at 16:38
  • 10
    @MykhayloAdamovych: No, that's actually not quite right. If `Branch` doesn't have a property which references `Company`, but the underlying table has a column which does, then you can use `@JoinTable` to map it. This is an unusual situation, because you would normally map the column in the object which corresponds to its table, but it can happen, and it is perfectly legitimate. – Tom Anderson Aug 13 '12 at 17:02
  • If you had a second class, say "NonProfitCompany" which also contained a List branches, how would the Branch map to both? Another private variable? – technocrat Nov 08 '13 at 22:52
  • @technocrat yes, another private variable of type `NonProfitCompany` would be required in class `Branch` – Óscar López Nov 08 '13 at 22:58
  • @ÓscarLópez I feel the urge to throw you a [a bat-signal](http://stackoverflow.com/questions/22486446/jpa-increment-a-numeric-field-through-a-sequence-programmatically) :P – Andrea Ligios Mar 21 '14 at 17:22
  • Very confusing sample, or simply wrong. The class Company must have ManyToOne annotated field and Branch a OneToMany one. – romsky Aug 05 '15 at 10:57
  • 4
    This is another reason not to like ORM's. The documentation is often too dodgy, and in my books, this is meandering on the too much magic territory. I've been struggling with this issue and when followed word by word for a `@OneToOne`, the child rows get updated with a `null` in their FKey column that references the parent. – Ashesh Mar 05 '16 at 17:59
  • Is foreign key a must have to setup this relationship? – Yang Dong Apr 10 '18 at 01:48
  • Taking the example, would be possible to replace in Branch entity, the Company object by the foreign key only? `private Long companyId;` then I should indicate `@ManyToOne(targetEntity = Company.class)` and the `@JoinColumn` would remain the same ? – anat0lius May 25 '18 at 11:44
  • 1
    @anat0lius not sure, I suggest you test it. But certainly, it'll break the object mapping, you should not map on column types, use the entity objects whenever possible. – Óscar López May 25 '18 at 12:59
  • 1
    @ÓscarLópez I tested, and it's strange, if I load the entity, it works, but it doesnt when I try to persist it. My strategy was to avoid using the object because I dont't want to eagger fetch it and using lazy fetching gives me a lot of pain. – anat0lius May 26 '18 at 21:15
  • 2
    `The annotation @JoinColumn indicates that this entity is the owner of the relationship` is **false** for unidirectional @OneToMany with @JoinColumn where fk is in the child entity. Example: https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/ – cdalxndr Apr 29 '20 at 17:48
239

@JoinColumn could be used on both sides of the relationship. The question was about using @JoinColumn on the @OneToMany side (rare case). And the point here is in physical information duplication (column name) along with not optimized SQL query that will produce some additional UPDATE statements.

According to documentation:

Since many to one are (almost) always the owner side of a bidirectional relationship in the JPA spec, the one to many association is annotated by @OneToMany(mappedBy=...)

@Entity
public class Troop {
    @OneToMany(mappedBy="troop")
    public Set<Soldier> getSoldiers() {
    ...
}

@Entity
public class Soldier {
    @ManyToOne
    @JoinColumn(name="troop_fk")
    public Troop getTroop() {
    ...
} 

Troop has a bidirectional one to many relationship with Soldier through the troop property. You don't have to (must not) define any physical mapping in the mappedBy side.

To map a bidirectional one to many, with the one-to-many side as the owning side, you have to remove the mappedBy element and set the many to one @JoinColumn as insertable and updatable to false. This solution is not optimized and will produce some additional UPDATE statements.

@Entity
public class Troop {
    @OneToMany
    @JoinColumn(name="troop_fk") //we need to duplicate the physical information
    public Set<Soldier> getSoldiers() {
    ...
}

@Entity
public class Soldier {
    @ManyToOne
    @JoinColumn(name="troop_fk", insertable=false, updatable=false)
    public Troop getTroop() {
    ...
}
donquih0te
  • 551
  • 3
  • 18
Mike
  • 17,033
  • 22
  • 85
  • 113
  • 1
    I am not able to figure out how Troop can be owner in your second snippet, Soldier is still the owner, as it contains foreign key referencing to Troop. (I am using mysql, I checked with your approach). – Akhilesh Oct 30 '13 at 07:46
  • this was taken 'as is' from documentation page (bold is mine) – Mike Nov 01 '13 at 17:43
  • 12
    In your exemple the annotation `mappedBy="troop"` refer to which field? – Fractaliste Mar 25 '14 at 09:16
  • 7
    @Fractaliste the annotation `mappedBy="troop"` refers to the property troop in the class Soldier. In the code above the property is not visible because here Mykhaylo omitted it, but you can deduce its existence by the getter getTroop(). Check the answer of [Óscar López](http://stackoverflow.com/a/11938290/589914), it is very clear and you will get the point. – nicolimo86 Sep 30 '15 at 19:49
  • 3
    This example is abuse of JPA 2 specification. If the objective of author is to create bidirectional relation then it should use mappedBy on parent side, and JoinColumn (if needed) on child side. With approach presented here we are getting 2 unidirectional relations: OneToMany and ManyToOne that are independent but just by luck (more by misuse) those 2 relations are defined using same foreign key – aurelije Dec 29 '16 at 10:32
  • 1
    If you're using JPA 2.x, my answer below is a little cleaner. Though I suggest trying both routes and seeing what Hibernate does when it generates the tables. If you're on a new project, pick whichever generation you think fits your needs. If you're on a legacy database and don't want to change the structure, pick whichever matches your schema. – Snekse Mar 07 '17 at 16:36
  • What is the meaning of 'physical mapping', as opposite to "virtual' mapping? – Treefish Zhang Dec 28 '17 at 02:55
  • Table definitions would help clarify where is the foreign key – cdalxndr Apr 29 '20 at 17:55
  • I tried to use the @JoinColumn in the parent table in my case, the table that does not have an FK and when I tried to save it, it is trying to create the child class first (the one which has the FK) and is throwing an error "Parent key not found". – Rafael Andrade Dec 17 '20 at 11:34
94

Unidirectional one-to-many association

If you use the @OneToMany annotation with @JoinColumn, then you have a unidirectional association, like the one between the parent Post entity and the child PostComment in the following diagram:

Unidirectional one-to-many association

When using a unidirectional one-to-many association, only the parent side maps the association.

In this example, only the Post entity will define a @OneToMany association to the child PostComment entity:

@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "post_id")
private List<PostComment> comments = new ArrayList<>();

Bidirectional one-to-many association

If you use the @OneToMany with the mappedBy attribute set, you have a bidirectional association. In our case, both the Post entity has a collection of PostComment child entities, and the child PostComment entity has a reference back to the parent Post entity, as illustrated by the following diagram:

Bidirectional one-to-many association

In the PostComment entity, the post entity property is mapped as follows:

@ManyToOne(fetch = FetchType.LAZY)
private Post post;

The reason we explicitly set the fetch attribute to FetchType.LAZY is because, by default, all @ManyToOne and @OneToOne associations are fetched eagerly, which can cause N+1 query issues.

In the Post entity, the comments association is mapped as follows:

@OneToMany(
    mappedBy = "post",
    cascade = CascadeType.ALL,
    orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();

The mappedBy attribute of the @OneToMany annotation references the post property in the child PostComment entity, and, this way, Hibernate knows that the bidirectional association is controlled by the @ManyToOne side, which is in charge of managing the Foreign Key column value this table relationship is based on.

For a bidirectional association, you also need to have two utility methods, like addChild and removeChild:

public void addComment(PostComment comment) {
    comments.add(comment);
    comment.setPost(this);
}

public void removeComment(PostComment comment) {
    comments.remove(comment);
    comment.setPost(null);
}

These two methods ensure that both sides of the bidirectional association are in sync. Without synchronizing both ends, Hibernate does not guarantee that association state changes will propagate to the database.

Which one to choose?

The unidirectional @OneToMany association does not perform very well, so you should avoid it.

You are better off using the bidirectional @OneToMany which is more efficient.

Vlad Mihalcea
  • 103,297
  • 39
  • 432
  • 788
32

The annotation mappedBy ideally should always be used in the Parent side (Company class) of the bi directional relationship, in this case it should be in Company class pointing to the member variable 'company' of the Child class (Branch class)

The annotation @JoinColumn is used to specify a mapped column for joining an entity association, this annotation can be used in any class (Parent or Child) but it should ideally be used only in one side (either in parent class or in Child class not in both) here in this case i used it in the Child side (Branch class) of the bi directional relationship indicating the foreign key in the Branch class.

below is the working example :

parent class , Company

@Entity
public class Company {


    private int companyId;
    private String companyName;
    private List<Branch> branches;

    @Id
    @GeneratedValue
    @Column(name="COMPANY_ID")
    public int getCompanyId() {
        return companyId;
    }

    public void setCompanyId(int companyId) {
        this.companyId = companyId;
    }

    @Column(name="COMPANY_NAME")
    public String getCompanyName() {
        return companyName;
    }

    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }

    @OneToMany(fetch=FetchType.LAZY,cascade=CascadeType.ALL,mappedBy="company")
    public List<Branch> getBranches() {
        return branches;
    }

    public void setBranches(List<Branch> branches) {
        this.branches = branches;
    }


}

child class, Branch

@Entity
public class Branch {

    private int branchId;
    private String branchName;
    private Company company;

    @Id
    @GeneratedValue
    @Column(name="BRANCH_ID")
    public int getBranchId() {
        return branchId;
    }

    public void setBranchId(int branchId) {
        this.branchId = branchId;
    }

    @Column(name="BRANCH_NAME")
    public String getBranchName() {
        return branchName;
    }

    public void setBranchName(String branchName) {
        this.branchName = branchName;
    }

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="COMPANY_ID")
    public Company getCompany() {
        return company;
    }

    public void setCompany(Company company) {
        this.company = company;
    }


}
Soumyaansh
  • 7,482
  • 6
  • 36
  • 43
26

I disagree with the accepted answer here by Óscar López. That answer is inaccurate!

It is NOT @JoinColumn which indicates that this entity is the owner of the relationship. Instead, it is the @ManyToOne annotation which does this (in his example).

The relationship annotations such as @ManyToOne, @OneToMany and @ManyToMany tell JPA/Hibernate to create a mapping. By default, this is done through a seperate Join Table.


@JoinColumn

The purpose of @JoinColumn is to create a join column if one does not already exist. If it does, then this annotation can be used to name the join column.


MappedBy

The purpose of the MappedBy parameter is to instruct JPA: Do NOT create another join table as the relationship is already being mapped by the opposite entity of this relationship.



Remember: MappedBy is a property of the relationship annotations whose purpose is to generate a mechanism to relate two entities which by default they do by creating a join table. MappedBy halts that process in one direction.

The entity not using MappedBy is said to be the owner of the relationship because the mechanics of the mapping are dictated within its class through the use of one of the three mapping annotations against the foreign key field. This not only specifies the nature of the mapping but also instructs the creation of a join table. Furthermore, the option to suppress the join table also exists by applying @JoinColumn annotation over the foreign key which keeps it inside the table of the owner entity instead.

So in summary: @JoinColumn either creates a new join column or renames an existing one; whilst the MappedBy parameter works collaboratively with the relationship annotations of the other (child) class in order to create a mapping either through a join table or by creating a foreign key column in the associated table of the owner entity.

To illustrate how MapppedBy works, consider the code below. If MappedBy parameter were to be deleted, then Hibernate would actually create TWO join tables! Why? Because there is a symmetry in many-to-many relationships and Hibernate has no rationale for selecting one direction over the other.

We therefore use MappedBy to tell Hibernate, we have chosen the other entity to dictate the mapping of the relationship between the two entities.

@Entity
public class Driver {
    @ManyToMany(mappedBy = "drivers")
    private List<Cars> cars;
}

@Entity
public class Cars {
    @ManyToMany
    private List<Drivers> drivers;
}

Adding @JoinColumn(name = "driverID") in the owner class (see below), will prevent the creation of a join table and instead, create a driverID foreign key column in the Cars table to construct a mapping:

@Entity
public class Driver {
    @ManyToMany(mappedBy = "drivers")
    private List<Cars> cars;
}

@Entity
public class Cars {
    @ManyToMany
    @JoinColumn(name = "driverID")
    private List<Drivers> drivers;
}
IqbalHamid
  • 1,570
  • 1
  • 12
  • 15
  • Very good answer, imho better than the accepted one. I just wonder why I still have foreign key columns and no join tables, even though I never used `@JoinColumn`. – Hannes Schneidermayer Jun 14 '20 at 06:28
  • Ok, seems that `@JoinColumn` is not needed to avoid the join table. Declaring both sides with annotations + one side with mappedBy will also introduce this behaviour. – Hannes Schneidermayer Jun 14 '20 at 06:49
20

I'd just like to add that @JoinColumn does not always have to be related to the physical information location as this answer suggests. You can combine @JoinColumn with @OneToMany even if the parent table has no table data pointing to the child table.

How to define unidirectional OneToMany relationship in JPA

Unidirectional OneToMany, No Inverse ManyToOne, No Join Table

It seems to only be available in JPA 2.x+ though. It's useful for situations where you want the child class to just contain the ID of the parent, not a full on reference.

Community
  • 1
  • 1
Snekse
  • 14,360
  • 10
  • 53
  • 73
1

JPA is a layered API, the different levels have their own annotations. The highest level is the (1) Entity level which describes persistent classes then you have the (2) relational database level which assume the entities are mapped to a relational database and (3) the java model.

Level 1 annotations: @Entity, @Id, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany. You can introduce persistency in your application using these high level annotations alone. But then you have to create your database according to the assumptions JPA makes. These annotations specify the entity/relationship model.

Level 2 annotations: @Table, @Column, @JoinColumn, ... Influence the mapping from entities/properties to the relational database tables/columns if you are not satisfied with JPA's defaults or if you need to map to an existing database. These annotations can be seen as implementation annotations, they specify how the mapping should be done.

In my opinion it is best to stick as much as possible to the high level annotations and then introduce the lower level annotations as needed.

To answer the questions: the @OneToMany/mappedBy is nicest because it only uses the annotations from the entity domain. The @oneToMany/@JoinColumn is also fine but it uses an implementation annotation where this is not strictly necessary.

donquih0te
  • 551
  • 3
  • 18
Bruno Ranschaert
  • 6,771
  • 4
  • 34
  • 42
1

Let me make it simple.
You can use @JoinColumn on either sides irrespective of mapping.

Let's divide this into three cases.
1) Uni-directional mapping from Branch to Company.
2) Bi-direction mapping from Company to Branch.
3) Only Uni-directional mapping from Company to Branch.

So any use-case will fall under this three categories. So let me explain how to use @JoinColumn and mappedBy.
1) Uni-directional mapping from Branch to Company.
Use JoinColumn in Branch table.
2) Bi-direction mapping from Company to Branch.
Use mappedBy in Company table as describe by @Mykhaylo Adamovych's answer.
3)Uni-directional mapping from Company to Branch.
Just use @JoinColumn in Company table.

@Entity
public class Company {

@OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY)
@JoinColumn(name="courseId")
private List<Branch> branches;
...
}

This says that in based on the foreign key "courseId" mapping in branches table, get me list of all branches. NOTE: you can't fetch company from branch in this case, only uni-directional mapping exist from company to branch.