How to save both parent and child records on saving parent record in JPA/hibernate?

Let’s say you have created an application to post blogs.

You are allowing users to comment on the blogs.

Every blog is associated with many comments.

You have chosen spring data as your backend technology.

How would you design this ?

You can create an entity class representing a blog post , say Blogpost.java

And then you can create another entity class representing a comment , say Comment.java

You then define the one to many relationship between blogpost and comment using @OneToMany annotation on the parent entity (Blogpost) and @ManyToOne annotation on the child entity (Comment).

Now let’s say you write code to create a blogpost ,add some comments to it and save it to database.

Would Hibernate save both parent and child entities on saving the parent record by default?

It won’t.

You need to set cascadeType attribute when you define @OneToMany annotation on the parent.

It should be set to CascadeType.PERSIST so that when a parent is persisted to database , then the child entity also gets persisted.

Let’s look at the code:

Here is the Blogpost entity:

package com.hibernate.childrecords;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@Entity
public class Blogpost {



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

    private String title;

    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
    List<Comment> commentList = new ArrayList<>();


    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public List<Comment> getCommentList() {
        return commentList;
    }

    public void setCommentList(List<Comment> commentList) {
        this.commentList = commentList;
    }

    @Override
    public String toString() {
        return "Blogpost{" +
                "id=" + id +
                " title=" + title +
                " comment=" + commentList +

                '}';
    }
}

The comment objects are associated with the Blogpost object using @OneToMany annotation.

Notice that I have added cascadeType as CascadeType.PERSIST. This will make sure both parent and child entities are persisted to database when parent is saved.

Also I have added the attribute fetch = FetchType.EAGER in the @OneToMany annotation. This is added for the comments associated with a blogpost to be fetched as soon as the blogpost entity is fetched so that they can be printed in the toString() method . If you don’t want to print the comments in the toString() method you can ignore this. By default hibernate will lazily load the child entities.

Below is the child entity :

package com.hibernate.childrecords;

import javax.persistence.*;

@Entity
public class Comment {

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

    private String message;

    @ManyToOne
    private Blogpost blogpost;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public Blogpost getBlogpost() {
        return blogpost;
    }

    public void setBlogpost(Blogpost blogpost) {
        this.blogpost = blogpost;
    }

    @Override
    public String toString() {
        return "Comment{" +
                "id=" + id +
                "comment=" + message +
                '}';
    }
}

As you see a field to represent the parent entity Blogpost is declared at the class level and is annotated with @ManyToOne annotation. It automatically associates the current entity to the blogpost entity.

For both the entities I am letting hibernate decide what strategy to use to generate the primary key by annotating the primary key field with @GeneratedValue(strategy = GenerationType.AUTO)

In case you are manually generating the primary keys then cascadeType = CascadeType.PERSIST will not work . CascadeType.MERGE should be used in such cases . Or CascadeType.ALL can be used in both the cases as it includes all the cascade scenarios.

Let’s look at the repository code provided by Spring Data:

package com.hibernate.childrecords;

import org.springframework.data.repository.CrudRepository;

public interface BlogRepository extends CrudRepository<Blogpost,Long> {
}

As you see there is almost no code there. Spring Data abstracts away all the database operations.

Let’s look at the client code.

I have used SpringBoot’s ApplicationRunner interface to test the functionality as soon as the application starts:

package com.hibernate.childrecords;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ChildrecordsApplication implements ApplicationRunner {


    @Autowired
    private BlogRepository blogRepository;


    public static void main(String[] args) {
        SpringApplication.run(ChildrecordsApplication.class, args);
    }


    @Override
    public void run(ApplicationArguments args) throws Exception {

        Blogpost post = new Blogpost();

        post.setTitle("First post");


        Comment comment = new Comment();

        comment.setMessage("My first comment");
        comment.setBlogpost(post);


        post.getCommentList().add(comment);


        blogRepository.save(post);


        Iterable<Blogpost> blogpostList = blogRepository.findAll();


        for (Blogpost blogpost : blogpostList) {

            System.out.println(blogpost);
        }


    }
}

The code is self explanatory. I am creating a blogpost object, a comment object , adding the comment object to the blogpost object and then call save on an instance of blogpost repository object.

That saves both the blogpost and its comment to the database.

Then I am fetching all blogposts and printing them to the console.

Below is the output:

Blogpost{id=1 title=First post comment=[Comment{id=1comment=My first comment}]}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s