Say we have a Post and Comment, like on social media — one post can have multiple comments,
while a comment belongs to just one post.
@Entitypublic class Post { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id;
private String title;
private String content;
// getters and setters}@Entitypublic class Comment { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id;
private String content;
// getters and setters}There are 3 ways to model a one-to-many relationship.
1. Unidirectional from the Comment side
We define the relationship in Comment, through the Post object — you work through the object,
not the raw id. Since many comments belong to one post, use @ManyToOne:
// in our Comment class@ManyToOne@JoinColumn(name = "post") // optional, renames the foreign keyprivate Post post;Here Post doesn’t know it’s in a relationship with Comment, so DB queries have to come from
the Comment side.
2. Unidirectional from the Post side
(The @ManyToOne in Comment stays unchanged, for this example.)
Here we define the relationship from the opposite/owning side — we want Post to explicitly own
its comments, using @OneToMany (works on a Collection like List or Set):
@OneToManyprivate List<Comment> comments;This is still technically a OneToMany relationship, but when you persist these entities you’ll
see an extra post_comment table appear, as if the DB treated it as ManyToMany. That’s because
Hibernate has no way to know how to create the foreign key when the relationship is declared from
the opposite side without an owner specified — so post_comment ends up as a join table.
In practice, never define a unidirectional relationship from both sides. If you do end up here,
specify the join column explicitly with @JoinColumn — it should live on the owning side of the
relationship (ownership = wherever the foreign key lives):
@OneToMany@JoinColumn(name = "post_id")private List<Comment> comments; // there should be no @ManyToOne in the Comment entity in this setup3. Bidirectional — both entities know about each other
// in our post class@OneToMany(mappedBy = "post")private List<Comment> comments;// in our comment class (the owner)@ManyToOne@JoinColumn(name = "post")private Post post;Whenever you have a bidirectional relationship, add mappedBy on the non-owning side, naming the
field on the other side.
A bidirectional relationship needs to be correctly set on both instances — setting only one side means the JPA implementation can’t guarantee it works, since it’s incomplete:
try { em.getTransaction().begin();
Post p = new Post(); p.setTitle("Post 1"); p.setContent("Post 1 desc");
Comment c1 = new Comment(); c1.setContent("Content comment 1");
p.setComments(List.of(c1)); // add comments to a post c1.setPost(p); // add a post to a comment
// persist both, since it doesn't persist by default em.persist(c1); em.persist(p);} finally { em.close();}Cascading
Whether it’s @OneToMany or @ManyToOne, cascading can be defined from either side. To persist
comments whenever a post is saved, define it on the Post entity:
@OneToMany(mappedBy = "post", cascade = CascadeType.PERSIST)private List<Comment> comments;Now, even without calling persist() on the Comment instance directly, it’s saved to the DB
anyway.