Through this tutorial, I will guide you how to implement a parent-child mapping using JPA annotations with Hibernate framework. A parent/child relationship is needed to represent nested, hierarchical structures like categories, as shown in the following picture:

hierarchy

As you can see, a category can have one or many children categories below it. And vice-versa, a category can have one or many parent categories above it – forming a nested, hierarchical structure. There’s no limit for the nested level.

So in this tutorial, you will learn how to use Hibernate framework to map a parent-child relationship for categories.

 

1. Design Database Table

Create the category table in the database with the following structure:

table category

As you can see, the column parent_id is a foreign key that refers to the primary key column category_id of the table itself. That forms a parent-child relationship. You can execute the following MySQL script to create this table:

CREATE TABLE `category` (
  `category_id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(128) NOT NULL,
  `parent_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`category_id`),
  KEY `parent_id_key` (`parent_id`),
  CONSTRAINT `parent_id_fk` FOREIGN KEY (`parent_id`) REFERENCES `category` (`category_id`)
);
Note that if a category with parent_id is null – meaning that the category has no parent – it’s become the top-level or root category.



 

2. Code Entity Class for Parent-Child Mapping

To map a parent-child relationship, the entity class should have a reference to its direct parent (one to one relationship) and a set of its children (one to many relationship) – as depicted in the following class diagram:

class diagram

So write code for the entity class Category as follows:

package net.codejava;

import java.util.*;

import javax.persistence.*;

@Entity
@Table
public class Category {
	@Id
	@Column(name = "category_id")
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;
	
	@Column(length = 64)
	private String name;
	
	@OneToOne
	@JoinColumn(name = "parent_id")
	private Category parent;
	
	@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
	private Set<Category> children = new HashSet<>();
	
	public Category(String name, Category parent) {
		this.name = name;
		this.parent = parent;
	}
	
	public Category(String name) {
		this.name = name;
	}	

	public Category() {
	}

	public Integer getId() {
		return id;
	}

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

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Category getParent() {
		return parent;
	}

	public void setParent(Category parent) {
		this.parent = parent;
	}

	public Set<Category> getChildren() {
		return children;
	}

	public void setChildren(Set<Category> children) {
		this.children = children;
	}
	
	public void addChild(Category children) {
		this.children.add(children);
	}
}
We use @OneToOne annotation to refer a category its parent:

@OneToOne
@JoinColumn(name = "parent_id")
private Category parent;
And to refer a category to its children, we use the @OneToMany annotation:

@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private Set<Category> children = new HashSet<>();
In this kind of parent/child relationship, the entity class has references to itself so it is also called self-referenced entity mapping.

 

3. Code Test Program for Parent-Child Relationship

Now, let’s write code to create the categories structure as shown above and then list all the categories recursively.

Below is the code snippet for persisting categories with parent-child mapping:

StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
		.configure()
		.build();
SessionFactory factory = new MetadataSources(registry)
		.buildMetadata()
		.buildSessionFactory();

Session session = factory.openSession();

Category electronics = new Category("Electronics");
Category mobilePhones = new Category("Mobile phones", electronics);
Category washingMachines = new Category("Washing machines", electronics);

electronics.addChild(mobilePhones);
electronics.addChild(washingMachines);

Category iPhone = new Category("iPhone", mobilePhones);
Category samsung = new Category("Samsung", mobilePhones);

mobilePhones.addChild(iPhone);
mobilePhones.addChild(samsung);

Category galaxy = new Category("Galaxy", samsung);
samsung.addChild(galaxy);

session.save(electronics);

session.close();
factory.close();
And below is code snippet for listing all categories from the database – print them in parent-child hierarchy structure:

private static void listCategories(Session session) {
	Category electronics = session.get(Category.class, 1);

	Set<Category> children = electronics.getChildren();
	
	System.out.println(electronics.getName());
	
	for (Category child : children) {
		System.out.println("--" + child.getName());
		printChildren(child, 1);
	}		
}

private static void printChildren(Category parent, int subLevel) {
	Set<Category> children = parent.getChildren();
	
	for (Category child : children) {
		for (int i = 0; i <= subLevel; i++) System.out.print("--");
				
		System.out.println(child.getName());
		
		printChildren(child, subLevel + 1);
	}
}
It will print the following nice output:

list categories

Conclusion

So that you’ve learned how to implement a parent-child relationship with Hibernate framework, or self-referenced entity mapping. It’s useful for nested structure like categories with parent and children.

You can also watch the video version of this tutorial and download the sample project code below. Or get the code examples on GitHub here.

 

Other Hibernate Tutorials:


About the Author:

is certified Java programmer (SCJP and SCWCD). He began programming with Java back in the days of Java 1.4 and has been passionate about it ever since. You can connect with him on Facebook and watch his Java videos on YouTube.



Attachments:
Download this file (HibernateParentChildDemo.zip)HibernateParentChildDemo.zip[Sample Hibernate project]13 kB

Add comment

   


Comments 

#3saif2023-04-30 10:38
Is not working with:
jakarta.persistence

An update will be nice...
Quote
#2DamQ2023-03-03 03:59
It gives me 'One To One' attribute has incorrect opposite 'One To Many' attribute 'childs'
Quote
#1Alex2022-08-25 01:14
Seemed that you have a error in mapping

@ManyToOne
@JoinColumn(name = "parent_id")
private Category parent;

Anyway it doesn't work for me. But thank you for the article)
Quote