Logo

dev-resources.site

for different kinds of informations.

Unidirectional associations for one-to-many

Published at
11/30/2024
Categories
webdev
database
jpa
hibernate
Author
hasebul_hassanchowdhury_
Categories
4 categories in total
webdev
open
database
open
jpa
open
hibernate
open
Author
24 person written this
hasebul_hassanchowdhury_
open
Unidirectional associations for one-to-many

What is a bidirectional relationship in relational database model? let's take a quick look -
Suppose you two table Post and Post_Comment. Now you want to find out which comment are associated with which post or for a post how many comments are associated with that? How you can do that?
Image description

Here is the basic Post entity table

@Entity
public class Post {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column
    private String title;

    public Post() {

    }
// ignoring setter and getter
}
Enter fullscreen mode Exit fullscreen mode

Here is the basic Post comment entity table

@Entity
public class PostComment {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    @Column
    private String description;

    public PostComment() {

    }
// ignoring setter and getter
Enter fullscreen mode Exit fullscreen mode

Currently, there is no relationship between the Post and PostComment entities. Now, let's establish a unidirectional relationship between them.

Key Cases to Consider:
Post as the Owner (One-To-Many):
This means the Post entity will manage the relationship. Each Post can have multiple PostComment entries.

PostComment as the Owner (Many-To-One):
Here, the PostComment entity will manage the relationship. Each comment will be linked to a single Post.

Let’s start by implementing Case 1: Post as the Owner.

@Entity
public class Post {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column
    private String title;

    @OneToMany(cascade = CascadeType.PERSIST, orphanRemoval = true)
    private List<PostComment> postComments = new ArrayList<>();

    public Post() {

    }

    public void addComment(PostComment comment) {
        postComments.add(comment);
    }

    public void removeComment(PostComment comment) {
        postComments.remove(comment);
    }
// ignoring setter and getter
}
Enter fullscreen mode Exit fullscreen mode

Now what we have done here?
We added a list and telling Post that it can have zero, one or more than one post comment by using @OneToMany. There can be mutiple comment associated with a single Post. we are cascade = CascadeType.PERSIST, orphanRemoval = true in these two later.
@OneToMany(cascade = CascadeType.PERSIST, orphanRemoval = true)
private List<PostComment> postComments = new ArrayList<>();

we also added two method which will make our life easier to add/remove comment

        public void addComment(PostComment comment) {
        postComments.add(comment);
    }

    public void removeComment(PostComment comment) {
        postComments.remove(comment);
    }
Enter fullscreen mode Exit fullscreen mode

Okay, we are almost done with our entity design (Not fully done). Let's take a look how we can save/persist a post with a comment.

@Service
public class PostService {

    @Autowired
    EntityManager eManager;

    @Transactional
    public void addPost1() {
        Post post = new Post();
        post.setTitle("Hibernate in action is quite good");

        PostComment comment = new PostComment();
        comment.setDescription("Recommended");

        post.addComment(comment);

        eManager.persist(post);
    }
}
Enter fullscreen mode Exit fullscreen mode

Here we are creating a post and then setting the title and adding a comment. Then calling eManager.persist(post) which will save the post and its associated comment. Easy, right!!!. Lets take a look at the database (H2) :

Image description

We can see a Post table which have only one row with id and title where id is 1 and title is "Hibernate in action is quite good".
Now lets look at post comment table :

Image description
We have a row with an id of 1 and a description as Recommended. Great! But what is the POST_POST_COMMENTS table? We didn’t explicitly define it in our codebase.

Here’s the explanation:
When we set up a one-to-many relationship between Post and Comment, each comment is linked to a Post using a post_id. This creates a mapping between post_id and post_comment_id. But where is this mapping stored?

Hibernate automatically creates a join table, combining the names of the two entities/tables (POST and POST_COMMENTS). This table holds two columns:

The primary key (POST_ID) from the Post table.
The primary key (POST_COMMENT_ID) from the Post_Comments table.
In this way, Hibernate manages the relationship without requiring us to define this table explicitly in the code..

Join Table

Can we improve that? An extra table is an extra burden.yes we can. lets see how we can do that?

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

Enter fullscreen mode Exit fullscreen mode

@JoinColumn(name = "post_id") This join column annotation tells that we don't need any extra table for mapping. Instead, POST_COMMENT table will handle it. There will be an extra column named "post_id" in POST_COMMENT table, which will store the associated post id for that comment, but good things is JPA/hibernate will do that on its own. We don't need to change our post comment entity.

POST_COMMENT table with POST_ID

lets add another comment in post with id 1.

@Transactional
    public void addCommentOnPost() {
        Post post = eManager.find(Post.class, 1);

        PostComment comment = new PostComment();
        comment.setDescription("this book is also for beginner");
        post.addComment(comment);

        eManager.persist(post);
    }
Enter fullscreen mode Exit fullscreen mode

Image description

Now lets try to fetch those comments.

@Transactional
    public void findCommentByPostId() {
        Post post = eManager.find(Post.class, 1);
        System.out.println("Number of comment " + post.getPostComments().size());
        System.out.println(post.getPostComments());
    }
/*
Number of comment 2
[
PostComment [id=1, description=Recommended], 
PostComment [id=2, description=this book is also for beginner]
]
*/
Enter fullscreen mode Exit fullscreen mode

How JPA/Hibernate fetches comments:
When you call post.getPostComments(), JPA/Hibernate queries the POST_COMMENT table. It checks the post_id column for entries that match the post_id (in our case, post_id=1). It then retrieves all matching rows and maps them to PostComment objects. That's how both comments associated with the post are fetched and returned

orphanRemoval = true
This ensures that if a PostComment is removed from the postComments list, it is automatically deleted from the database. Essentially:

  1. If a comment is no longer associated with a Post, it becomes an orphan and is removed from the database.
Post post = entityManager.find(Post.class, 1);
PostComment comment = post.getPostComments().get(0);

post.getPostComments().remove(comment);
Enter fullscreen mode Exit fullscreen mode

Without orphanRemoval = true, the PostComment would still exist in the database even if it is removed from the list, potentially leading to orphaned rows.

CascadeType.PERSIST
This means that whenever you persist a Post entity, all associated PostComment entities will also be automatically persisted. In other words:

  1. You don't need to explicitly save the comments.
  2. As long as they are added to the postComments list and the Post is persisted, Hibernate will ensure the comments are saved to the database.
Post post = new Post();
PostComment comment = new PostComment();
comment.setDescription("A new comment");

post.getPostComments().add(comment);

entityManager.persist(post);

Enter fullscreen mode Exit fullscreen mode

Without CascadeType.PERSIST, you would need to explicitly persist each PostComment

hibernate Article's
30 articles in total
Favicon
JOOQ Is Not a Replacement for Hibernate. They Solve Different Problems
Favicon
Persistence Context в Hibernate Zoo: путешествие объекта по жизненным состояниям
Favicon
Unidirectional associations for one-to-many
Favicon
Como eu reduzi em até 99% o tempo de resposta da minha API
Favicon
Hibernate Zoo: Жадный Гиппопотам и Ленивый Лемур (Lazy vs Eager)
Favicon
🐾 Hibernate Zoo: Путеводитель по языкам запросов в мире данных 🐾
Favicon
How To Fetch Data By Using DTO Projection In Spring Data JPA
Favicon
Ubuntu 22.04 Hibernate Using Swap File
Favicon
Зоопарк Hibernate: N+1 запросов или как накормить жадного бегемота
Favicon
Spring Data JPA Stream Query Methods
Favicon
Uma breve introdução ao Hibernate
Favicon
Ubuntu hibernate
Favicon
Eager vs Lazy Initialization of Spring Beans
Favicon
Understanding JPA Mappings in Spring Boot: One-to-One, One-to-Many, Many-to-One, and Many-to-Many Relationships
Favicon
Java Hibernate vs JPA: Rapid review for you
Favicon
Hibernate Connection Library with GUI Generation
Favicon
what is JPA? explain few configurations
Favicon
Demystifying Hibernate: A Beginner's Journey
Favicon
How to deal with N+1 problems with Hibernate
Favicon
Java Hibernate vs JPA: Quick Review
Favicon
Uppercase table names in Spring Boot
Favicon
Hiring Alert - Java Developer- Blockchain
Favicon
H2 database Setup Error Unable to load name org.hibernate.dialect.Oracle10gDialect
Favicon
Capitalisation of table name generated by Hibernate when using MySQL server
Favicon
Display SQL statement generated by Hibernate JPA in Spring Boot environment
Favicon
Advanced and dynamic searching with Spring Data JPA
Favicon
Defining JPA/Hibernate Entities in Kotlin
Favicon
Criteria Queries and JPA Metamodel with Spring Boot and Kotlin
Favicon
How to save Hibernate Entity changes to Database
Favicon
Hibernate Cheat Sheet

Featured ones: