In which case do you use the JPA @JoinTable
annotation?

- 142,745
- 71
- 566
- 911

- 2,923
- 7
- 28
- 37
5 Answers
EDIT 2017-04-29: As pointed to by some of the commenters, the JoinTable
example does not need the mappedBy
annotation attribute. In fact, recent versions of Hibernate refuse to start up by printing the following error:
org.hibernate.AnnotationException:
Associations marked as mappedBy must not define database mappings
like @JoinTable or @JoinColumn
Let's pretend that you have an entity named Project
and another entity named Task
and each project can have many tasks.
You can design the database schema for this scenario in two ways.
The first solution is to create a table named Project
and another table named Task
and add a foreign key column to the task table named project_id
:
Project Task
------- ----
id id
name name
project_id
This way, it will be possible to determine the project for each row in the task table. If you use this approach, in your entity classes you won't need a join table:
@Entity
public class Project {
@OneToMany(mappedBy = "project")
private Collection<Task> tasks;
}
@Entity
public class Task {
@ManyToOne
private Project project;
}
The other solution is to use a third table, e.g. Project_Tasks
, and store the relationship between projects and tasks in that table:
Project Task Project_Tasks
------- ---- -------------
id id project_id
name name task_id
The Project_Tasks
table is called a "Join Table". To implement this second solution in JPA you need to use the @JoinTable
annotation. For example, in order to implement a uni-directional one-to-many association, we can define our entities as such:
Project
entity:
@Entity
public class Project {
@Id
@GeneratedValue
private Long pid;
private String name;
@JoinTable
@OneToMany
private List<Task> tasks;
public Long getPid() {
return pid;
}
public void setPid(Long pid) {
this.pid = pid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Task> getTasks() {
return tasks;
}
public void setTasks(List<Task> tasks) {
this.tasks = tasks;
}
}
Task
entity:
@Entity
public class Task {
@Id
@GeneratedValue
private Long tid;
private String name;
public Long getTid() {
return tid;
}
public void setTid(Long tid) {
this.tid = tid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
This will create the following database structure:
The @JoinTable
annotation also lets you customize various aspects of the join table. For example, had we annotated the tasks
property like this:
@JoinTable(
name = "MY_JT",
joinColumns = @JoinColumn(
name = "PROJ_ID",
referencedColumnName = "PID"
),
inverseJoinColumns = @JoinColumn(
name = "TASK_ID",
referencedColumnName = "TID"
)
)
@OneToMany
private List<Task> tasks;
The resulting database would have become:
Finally, if you want to create a schema for a many-to-many association, using a join table is the only available solution.

- 46,888
- 25
- 118
- 160
-
1using the first approach I have my Project filled with my tasks and each Task filled with parent Project before the merge and works but all my entries are duplicated based on the number of my tasks. A Project with two tasks are saved twice in my database. Why ? – MaikoID Dec 12 '12 at 20:42
-
**UPDATE** There aren't duplicates entries in my database, the hibernate is selecting with the left outer join and I dont know why.. – MaikoID Dec 12 '12 at 20:49
-
2I believe `@JoinTable/@JoinColumn` can be annotated on same field with `mappedBy`. So the correct example should be keeping the `mappedBy` in `Project`, and move the `@JoinColumn` to `Task.project` (or vice-versa) – Adrian Shum Mar 07 '13 at 06:42
-
How do I insert new rows in to Join table? Through an independent Entity for the join table? – vumaasha Oct 15 '13 at 14:19
-
Is the `@ManyToOne private Project project;` required in the `Task` entity for the mapping to work in `Project`? I see this in just about every Spring example and find that a child linking back to its parent creates too much dependency. – Jeach Dec 30 '14 at 06:09
-
2Nice! But I have a further question: if the join table `Project_Tasks` needs the `name` of `Task` as well, which becomes three columns: `project_id`,`task_id`,`task_name`, how to achieve this? – macemers Jan 13 '15 at 05:13
-
One of the clearest and simplest examples on the topic and actually on anything related to JPA and Entity-beans. I've wondered why people have to make complex examples on this topic always. +1 – Steve Waters Jan 28 '15 at 13:00
-
5I think you should not have mappedBy on your second usage example to prevent this error `Caused by: org.hibernate.AnnotationException: Associations marked as mappedBy must not define database mappings like @JoinTable or @JoinColumn:` – karthik m Nov 21 '15 at 04:17
-
This wont work until you change @OneToMany(mappedBy = "project") to @OneToMany in the latter case – Abdullah Khan Jan 07 '16 at 17:05
-
I think this answer should be edited since it misguides the reader about usage of mappedBy in the JoinTable annotation. The other user have also mentioned this. – Raj Feb 24 '17 at 18:07
-
@karthikm So where should we put `mappedBy` and why? – Bruce Sun Mar 13 '17 at 07:31
-
+1 best explanation I have seen. Just make it clear that the tasks property for join table is on the Project entity. – Al Grant May 08 '17 at 22:32
-
Question: what if i have already this additional table? The JoinTable wont overwrite the existign one right? – TheWandererr Sep 23 '17 at 17:38
-
@TheWandererr If you use Hibernate, that depends on what `hibernate.hbm2ddl.auto` configuration you choose: http://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#schema-generation. Other JPA implementations should have similar properties. – Behrang Sep 25 '17 at 05:28
-
@Behrang i am using Spring MVC and the ddl is not set on auto – TheWandererr Sep 25 '17 at 11:48
-
@TheWandererr Spring MVC, or Spring in general, by default uses Hibernate as the JPA provider. You can configure Hibernate's behaviour as documented [here](https://docs.spring.io/spring-boot/docs/current/reference/html/howto-database-initialization.html#howto-initialize-a-database-using-hibernate). – Behrang Oct 10 '17 at 00:58
-
Thanks in advance...I'm kinda new to this so please bear with me if I'm asking something dumb! what if there is a property in "MY_JT" Table like Date or something? how to represent this in hibernate? to make myself clear what if the relation between two models has a it's own property like what we have when we design ERD? – N-Alpr Jan 27 '18 at 04:13
-
Donno why in my case hibernate is creating a redundant column similar to project.proj_id in the project table. if anyone has any idea about it please guide? – SPS May 26 '20 at 13:24
@ManyToMany
associations
Most often, you will need to use @JoinTable
annotation to specify the mapping of a many-to-many table relationship:
- the name of the link table and
- the two Foreign Key columns
So, assuming you have the following database tables:
In the Post
entity, you would map this relationship, like this:
@ManyToMany(cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
@JoinTable(
name = "post_tag",
joinColumns = @JoinColumn(name = "post_id"),
inverseJoinColumns = @JoinColumn(name = "tag_id")
)
private List<Tag> tags = new ArrayList<>();
The @JoinTable
annotation is used to specify the table name via the name
attribute, as well as the Foreign Key column that references the post
table (e.g., joinColumns
) and the Foreign Key column in the post_tag
link table that references the Tag
entity via the inverseJoinColumns
attribute.
Notice that the cascade attribute of the
@ManyToMany
annotation is set toPERSIST
andMERGE
only because cascadingREMOVE
is a bad idea since we the DELETE statement will be issued for the other parent record,tag
in our case, not to thepost_tag
record.
Unidirectional @OneToMany
associations
The unidirectional @OneToMany
associations, that lack a @JoinColumn
mapping, behave like many-to-many table relationships, rather than one-to-many.
So, assuming you have the following entity mappings:
@Entity(name = "Post")
@Table(name = "post")
public class Post {
@Id
@GeneratedValue
private Long id;
private String title;
@OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();
//Constructors, getters and setters removed for brevity
}
@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment {
@Id
@GeneratedValue
private Long id;
private String review;
//Constructors, getters and setters removed for brevity
}
Hibernate will assume the following database schema for the above entity mapping:
As already explained, the unidirectional @OneToMany
JPA mapping behaves like a many-to-many association.
To customize the link table, you can also use the @JoinTable
annotation:
@OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true
)
@JoinTable(
name = "post_comment_ref",
joinColumns = @JoinColumn(name = "post_id"),
inverseJoinColumns = @JoinColumn(name = "post_comment_id")
)
private List<PostComment> comments = new ArrayList<>();
And now, the link table is going to be called post_comment_ref
and the Foreign Key columns will be post_id
, for the post
table, and post_comment_id
, for the post_comment
table.
Unidirectional
@OneToMany
associations are not efficient, so you are better off using bidirectional@OneToMany
associations or just the@ManyToOne
side.

- 142,745
- 71
- 566
- 911
-
Hi @Vlad, If the join table has no extra column(s), is it better to use the `@JoinTable` instead of the join entity? what is the advantage of the `@JoinTable` over the join entity? (or vice versa) – Arash Oct 21 '21 at 15:51
-
1Check out [this](https://vladmihalcea.com/the-best-way-to-use-the-manytomany-annotation-with-jpa-and-hibernate/) and [this](https://vladmihalcea.com/the-best-way-to-map-a-many-to-many-association-with-extra-columns-when-using-jpa-and-hibernate/) articles for a detailed answer to your question. – Vlad Mihalcea Oct 22 '21 at 05:19
-
I've read these articles before; They are great. But my fault, i missed the **Conclusion** part of [second article](https://vladmihalcea.com/the-best-way-to-map-a-many-to-many-association-with-extra-columns-when-using-jpa-and-hibernate/). That part is my answer. Thanks @Vlad. – Arash Oct 22 '21 at 07:26
-
When in doubt, just go to Vlad Mihalcea Dot Com. That's where the answers are. – Vlad Mihalcea Oct 22 '21 at 07:29
It's the only solution to map a ManyToMany association : you need a join table between the two entities tables to map the association.
It's also used for OneToMany (usually unidirectional) associations when you don't want to add a foreign key in the table of the many side and thus keep it independent of the one side.
Search for @JoinTable in the hibernate documentation for explanations and examples.
-
Hi, It's not the only solution for many to many association. You can create **join entity** with two bidirectional `@OneToMany` associations. – Arash Oct 21 '21 at 09:22
It's also cleaner to use @JoinTable
when an Entity could be the child in several parent/child relationships with different types of parents. To follow up with Behrang's example, imagine a Task can be the child of Project, Person, Department, Study, and Process.
Should the task
table have 5 nullable
foreign key fields? I think not...
It lets you handle Many to Many relationship. Example:
Table 1: post
post has following columns
____________________
| ID | DATE |
|_________|_________|
| | |
|_________|_________|
Table 2: user
user has the following columns:
____________________
| ID |NAME |
|_________|_________|
| | |
|_________|_________|
Join Table lets you create a mapping using:
@JoinTable(
name="USER_POST",
joinColumns=@JoinColumn(name="USER_ID", referencedColumnName="ID"),
inverseJoinColumns=@JoinColumn(name="POST_ID", referencedColumnName="ID"))
will create a table:
____________________
| USER_ID| POST_ID |
|_________|_________|
| | |
|_________|_________|

- 2,657
- 18
- 29
-
2Question: what if i have already this additional table? The JoinTable wont overwrite the existign one right? – TheWandererr Sep 22 '17 at 15:47
-
@TheWandererr did you find out the answer to your question? I have a join table already – asgs Oct 27 '17 at 11:50
-
In my case it's creating a redundant column in the owning side table. for eg. POST_ID in POST. Can you suggest why is it happening? – SPS May 26 '20 at 13:09