Perhaps one-to-many is one of the most commonly used associations in Object/Relational Mapping. The best practice is to use a separate join table for a one-to-many association to gain flexibility and scalability. Therefore, this Hibernate tutorial is dedicated for mapping such kind of association using JPA annotations, with a sample program will be developed in Eclipse and Maven, and the database is MySQL. This is an alternative approach to the XML descriptor as described in the tutorial: Hibernate One-to-Many Using Join Table XML Mapping Example.

A typical one-to-many association on join table is illustrated in the following diagram:

One to Many on a Join Table entity relationship

Here, a join table (category_article) is created to make the one-to-many association between the category and article tables.

To develop the sample program, we use the following pieces of software programs and libraries (you can use newer versions):

Table of content:

  1. Creating Database and Tables
  2. Creating Maven-Eclipse Project
  3. Using Annotations for One-to-Many Association on Join Table
  4. Writing Hibernate Configuration File
  5. Writing a Test Program
 

1. Creating Database and Tables

The following MySQL script is used to create a database called newsdb with three tables: category, article and category_article (join table):

CREATE DATABASE `newsdb`;

use newsdb;

CREATE TABLE `category` (
  `category_id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(45) NOT NULL,
  PRIMARY KEY (`category_id`)
);


CREATE TABLE `article` (
  `article_id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(70) NOT NULL,
  `description` varchar(250) NOT NULL,
  `keywords` varchar(150) NOT NULL,
  `content` text NOT NULL,
  PRIMARY KEY (`article_id`)
);

CREATE TABLE `category_article` (
  `category_id` int(11) NOT NULL,
  `article_id` int(11) NOT NULL,
  PRIMARY KEY (`category_id`,`article_id`),
  UNIQUE KEY `article_id_UNIQUE` (`article_id`),
  KEY `fk_category` (`category_id`),
  KEY `fk_article` (`article_id`),
  CONSTRAINT `fk_article` FOREIGN KEY (`article_id`) REFERENCES `article` (`article_id`),
  CONSTRAINT `fk_category` FOREIGN KEY (`category_id`) REFERENCES `category` (`category_id`)
);
Notice that the column article_id of the join table is set with unique key constraint to enforce the one-to-many association, meaning that there is no one article belongs to more than one category.



Thus we have the structure of the newsdb database as follows:

newsdb database structure

 


2. Creating Maven-Eclipse Project

In Eclipse IDE, create a Maven project named HibernateOne2ManyJoinTableAnnotationsExample with the following structure:

Hibernate One-to-Many Join Table Project Structure

This project consists of the following files:

  • Model classes: Category.java and Article.java
  • Hibernate XML configuration file: hibernate.cfg.xml
  • Test program: ArticlesManager.java
  • Maven project: pom.xml
And update the pom.xml file for the latest Hibernate release and MySQL Connector/J library as follows:

<project xmlns="http://maven.apache.org/POM/4.0.0" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
		http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>net.codejava.hibernate</groupId>
  <artifactId>HibernateOne2ManyJoinTableAnnotationsExample</artifactId>
  <version>1.0</version>
  <description>A sample Hibernate application that maps a one-to-many association
   on join table using annotations</description>
  <dependencies>
  	<dependency>
  		<groupId>org.hibernate</groupId>
  		<artifactId>hibernate-core</artifactId>
  		<version>4.2.7.SP1</version>
  	</dependency>
  	<dependency>
  		<groupId>mysql</groupId>
  		<artifactId>mysql-connector-java</artifactId>
  		<version>5.1.26</version>
  	</dependency>
  </dependencies>   
</project>
All other dependencies will be resolved automatically by Maven.


3. Using Annotations for One-to-Many Association on Join Table

To represent the one-to-many association in the object model, let’s create two classes called Category.java and Article.java as follows:

File net\codejava\hibernate\Category.java:

package net.codejava.hibernate;

import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "CATEGORY")
public class Category {

	private long id;
	private String name;

	private Set<Article> articles;

	public Category() {
	}

	public Category(String name) {
		this.name = name;
	}

	@Id
	@GeneratedValue
	@Column(name = "CATEGORY_ID")
	public long getId() {
		return id;
	}

	@OneToMany(cascade = CascadeType.ALL)
	@JoinTable(
			name = "CATEGORY_ARTICLE",
			joinColumns = @JoinColumn(name = "CATEGORY_ID"),
			inverseJoinColumns = @JoinColumn(name = "ARTICLE_ID")
	)

	// other getters and setters...

}
 

File net\codejava\hibernate\Article.java:

package net.codejava.hibernate;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "ARTICLE")
public class Article {
	private long id;
	private String title;
	private String description;
	private String keywords;
	private String content;

	public Article() {
	}

	public Article(String title, String description, String keywords,
			String content) {
		this.title = title;
		this.description = description;
		this.keywords = keywords;
		this.content = content;
	}

	@Id
	@GeneratedValue
	@Column(name = "ARTICLE_ID")
	public long getId() {
		return id;
	}

	// other getters and setters...
}
We can see that a Category has a set of Articles, but an Article doesn’t have any back reference to the Category, thus this association is unidirectional.

To map the one-to-many association on join table, these JPA annotations are used: @OneToMany, @JoinTable and @JoinColumn, besides the basic annotations (@Entity, @Column, @Id, etc) . Let’s look at the code in the Category side closely:

private Set<Article> articles;

@OneToMany(cascade = CascadeType.ALL)
@JoinTable(
		name = "CATEGORY_ARTICLE",
		joinColumns = @JoinColumn(name = "CATEGORY_ID"),
		inverseJoinColumns = @JoinColumn(name = "ARTICLE_ID")
)
public Set<Article> getArticles() {
	return articles;
}

Here, we use the @JoinTable annotation to specify the details of the join table (table name and two join columns - using the @JoinColumn annotation); and we set the cascade attribute of the @OneToMany annotation so that Hibernate will update the associated articles when the category is updated.

And note that we don’t have to use any special annotations for the Article side, as this association is unidirectional; and there is no need to create a mapping class for the join table.


4. Writing Hibernate Configuration File

To tell Hibernate which database to connect and what the mapping classes are, create the hibernate.cfg.xml file under the classpath with the following content:

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>        
  <session-factory>
    <!-- Database connection settings -->
    <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="connection.url">jdbc:mysql://localhost:3306/newsdb</property>
    <property name="connection.username">root</property>
    <property name="connection.password">secret</property>
    <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
    <property name="show_sql">true</property>
    
    <mapping class="net.codejava.hibernate.Category"/>
    <mapping class="net.codejava.hibernate.Article"/>
      
  </session-factory>
</hibernate-configuration>


5. Writing a Test Program

To test the association mapping we have made so far, let’s write a test program that saves some dummy data with the following code (ArticlesManager.java):

package net.codejava.hibernate;

import java.util.HashSet;
import java.util.Set;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;

/**
 * This program demonstrates how to use JPA annotations to map
 * a one-to-many association on join table in Hibernate.
 * @author www.codejava.net
 *
 */
public class ArticlesManager {

	public static void main(String[] args) {
		// loads configuration and mappings
		Configuration configuration = new Configuration().configure();
		ServiceRegistryBuilder registry = new ServiceRegistryBuilder();
		registry.applySettings(configuration.getProperties());
		ServiceRegistry serviceRegistry = registry.buildServiceRegistry();

		// builds a session factory from the service registry
		SessionFactory sessionFactory = configuration
				.buildSessionFactory(serviceRegistry);

		// obtains the session
		Session session = sessionFactory.openSession();
		session.beginTransaction();

		Category category = new Category("Hibernate Framework");

		Article articleOne = new Article("One-to-One Mapping",
				"One-to-One XML Mapping Tutorial", "Hibernate,One-to-One",
				"Content of One-to-One XML Mapping Tutorial");
		Article articleTwo = new Article("One-to-Many Mapping",
				"One-to-Many XML Mapping Tutorial", "Hibernate,One-to-Many",
				"Content of One-to-Many XML Mapping Tutorial");
		Article articleThree = new Article("Many-to-Many Mapping",
				"Many-to-Many XML Mapping Tutorial", "Hibernate,Many-to-Many",
				"Content of Many-to-Many XML Mapping Tutorial");

		Set<Article> articles = new HashSet<Article>();
		articles.add(articleOne);
		articles.add(articleTwo);
		articles.add(articleThree);

		category.setArticles(articles);

		session.save(category);

		session.getTransaction().commit();
		session.close();
	}

}
Output of the program:

Hibernate: insert into CATEGORY (name) values (?)
Hibernate: insert into ARTICLE (content, description, keywords, title) values (?, ?, ?, ?)
Hibernate: insert into ARTICLE (content, description, keywords, title) values (?, ?, ?, ?)
Hibernate: insert into ARTICLE (content, description, keywords, title) values (?, ?, ?, ?)
Hibernate: insert into CATEGORY_ARTICLE (CATEGORY_ID, ARTICLE_ID) values (?, ?)
Hibernate: insert into CATEGORY_ARTICLE (CATEGORY_ID, ARTICLE_ID) values (?, ?)
Hibernate: insert into CATEGORY_ARTICLE (CATEGORY_ID, ARTICLE_ID) values (?, ?)
Result in the category table:

records in category table

Result in the article table:

records in article table

Result in the join table category_article:

records in category article table

For your reference, you can check the code on GitHub, or download the sample project attached below. 

 

Related Hibernate One-to-Many Tutorials:

 

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 (HibernateOne2ManyJoinTableAnnotationsExample.zip)HibernateOne2ManyJoinTableAnnotationsExample.zip[Eclipse-Maven project]16 kB

Add comment

   


Comments 

#15Mike2022-06-06 13:00
This tutorial doesn't make sense. A one-to-many relationship doesn't need a joining table. In a one-to-many relationship the many side simply has a FK to a row on the one side.

Joining tables are only needed for many-to-many relationships.

Article simply needs a FK column for category_id that references a row in the category table.
Quote
#14Jagddesh2020-04-04 13:05
Hi,

If I want to get all articles with category name, how to get those details.
Quote
#13Anuj2019-07-12 00:59
What we should in case of update child list?
Quote
#12amitabh2018-06-05 07:59
Quoting Nam:
Quoting amitabh:
I have similar situation ...ame above code.

You need to handle extra columns in the join table, right?
So here's the way:

codejava.net/.../...

AWESOME . YOU WONT FIND THIS ANYWHERE EXPLANED IN SO DETAILS . appreciate it a lot.
Quote
#11Nam2018-06-03 22:32
Quoting amitabh:
I have similar situation ...ame above code.

You need to handle extra columns in the join table, right?
So here's the way:

codejava.net/.../...
Quote