Hibernate Many to Many Mapping using XML and Annotation Configurations

Today we will look into Many to Many Mapping using XML and annotation configurations. Earlier we looked how to implement One To One and One To Many mapping in Hibernate.

Hibernate Many to Many

Many-to-Many mapping is usually implemented in database using a Join Table. For example, we can have Cart and Item table and Cart_Items table for many-to-many mapping. Every cart can have multiple items and every item can be part of multiple carts, so we have a many to many mapping here.

Database Setup

Below script can be used to create our many-to-many example database tables, these scripts are for MySQL database. If you are using any other database, you might need to make small changes to get it working.

 
DROP TABLE IF EXISTS `Cart_Items`;
DROP TABLE IF EXISTS `Cart`;
DROP TABLE IF EXISTS `Item`;

CREATE TABLE `Cart` (
  `cart_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `cart_total` decimal(10,0) NOT NULL,
  PRIMARY KEY (`cart_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

CREATE TABLE `Item` (
  `item_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `item_desc` varchar(20) NOT NULL,
  `item_price` decimal(10,0) NOT NULL,
  PRIMARY KEY (`item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `Cart_Items` (
  `cart_id` int(11) unsigned NOT NULL,
  `item_id` int(11) unsigned NOT NULL,
  PRIMARY KEY (`cart_id`,`item_id`),
  CONSTRAINT `fk_cart` FOREIGN KEY (`cart_id`) REFERENCES `Cart` (`cart_id`),
  CONSTRAINT `fk_item` FOREIGN KEY (`item_id`) REFERENCES `Item` (`item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Notice that Cart_Items table doesn’t have any extra columns, actually it doesn’t make much sense to have extra columns in many-to-many mapping table. But if you have extra columns, the implementation changes little bit and we will look into that in another post. Below diagram shows the Entity relationship between these tables.


Our Database setup is ready now, let’s move on to create the hibernate many-to-many mapping project.

Many to Many Mapping Project Structure

Create a maven project in Eclipse or your favorite IDE, below image shows the structure and different components in the application.

We will first look into the XML based mapping implementations and then move over to using JPA annotations.

Hibernate Maven Dependencies

Our final pom.xml contains Hibernate dependencies with latest version 4.3.5.Final and MySQL driver dependencies.

 

<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.journaldev.hibernate</groupId>
<artifactId>HibernateManyToManyMapping</artifactId>
<version>0.0.1-SNAPSHOT</version>

<dependencies>
  <dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>4.3.5.Final</version>
  </dependency>
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.0.5</version>
  </dependency>
</dependencies>

</project>

Hibernate Many to Many XML Configuration Model Classes

Cart.java

 

package com.journaldev.hibernate.model;

import java.util.Set;

public class Cart {

    private long id;
    private double total;

    private Set items;
    
    public double getTotal() {
        return total;
    }

    public void setTotal(double total) {
        this.total = total;
    }

    public long getId() {
        return id;
    }

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

    public Set getItems() {
        return items;
    }

    public void setItems(Set items) {
        this.items = items;
    }

}

Item.java

 

package com.journaldev.hibernate.model;

import java.util.Set;

public class Item {

    private long id;
    private double price;
    private String description;

    private Set carts;
    
    public long getId() {
        return id;
    }

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

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Set getCarts() {
        return carts;
    }

    public void setCarts(Set carts) {
        this.carts = carts;
    }
}

Notice that Cart has a set of Item and Item has a set of Cart, this way we are implementing Bi-Directional associations. It means that we can configure it to save Item when we save Cart and vice versa. For one-directional mapping, usually we have set in one of the model classes. We will use annotations for one-directional mapping.

Many to Many Mapping XML Configuration

Let’s create Hibernate many-to-many mapping XML configuration files for Cart and Item. We will implement bi-directional many-to-many mapping.

cart.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
	"https://hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="com.journaldev.hibernate.model">
	<class name="Cart" table="CART">
		<id name="id" type="long">
			<column name="cart_id" />
			<generator class="identity" />
		</id>
		<property name="total" type="double" column="cart_total" />

		<set name="items" table="CART_ITEMS" fetch="select" cascade="all">
			<key column="cart_id" />
			<many-to-many class="Item" column="item_id" />
		</set>
	</class>

</hibernate-mapping>

Notice that the set of items is mapped to the CART_ITEMS table. Since Cart is the primary object, cart_id is the key and many-to-many mapping is using the Item class item_id column.

item.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
"https://hibernate.org/dtd/hibernate-mapping-3.0.dtd" >

<hibernate-mapping package="com.journaldev.hibernate.model">

	<class name="Item" table="ITEM">
		<id name="id" type="long">
			<column name="item_id" />
			<generator class="identity" />
		</id>
		<property name="description" type="string" column="item_desc" />

		<property name="price" type="double" column="item_price" />

		<set name="carts" table="CART_ITEMS" fetch="select" cascade="all">
			<key column="item_id" />
			<many-to-many class="Cart" column="cart_id" />
		</set>

	</class>

</hibernate-mapping>

As you can see from above, the mapping is very similar to Cart mapping configurations.

Hibernate Configuration for XML Based Many to Many Mapping

Our Hibernate configuration file looks like below.

hibernate.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
		"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
		"https://hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
	<session-factory>
		<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
		<property name="hibernate.connection.password">pankaj123</property>
		<property name="hibernate.connection.url">jdbc:mysql://localhost/TestDB</property>
		<property name="hibernate.connection.username">pankaj</property>
		<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>

		<property name="hibernate.current_session_context_class">thread</property>
		<property name="hibernate.show_sql">true</property>

		<mapping resource="cart.hbm.xml" />
		<mapping resource="item.hbm.xml" />
	</session-factory>
</hibernate-configuration>

Hibernate SessionFactory Utility Class for XML Based Mapping

Here’s a utility class that works as a factory for SessionFactory.

HibernateUtil.java

package com.journaldev.hibernate.util;

import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;

public class HibernateUtil {

	private static SessionFactory sessionFactory;

	private static SessionFactory buildSessionFactory() {
		try {
			// Create the SessionFactory from hibernate.cfg.xml
			Configuration configuration = new Configuration();
			configuration.configure("hibernate.cfg.xml");
			System.out.println("Hibernate Configuration loaded");

			ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
					.applySettings(configuration.getProperties()).build();
			System.out.println("Hibernate serviceRegistry created");

			SessionFactory sessionFactory = configuration
					.buildSessionFactory(serviceRegistry);

			return sessionFactory;
		} catch (Throwable ex) {
			System.err.println("Initial SessionFactory creation failed." + ex);
			ex.printStackTrace();
			throw new ExceptionInInitializerError(ex);
		}
	}

	public static SessionFactory getSessionFactory() {
		if (sessionFactory == null)
			sessionFactory = buildSessionFactory();
		return sessionFactory;
	}

}

Hibernate Many To Many Mapping XML Configuration Test Program

Our hibernate many-to-many mapping setup is ready, let’s test it out. We will write two programs, one to save Cart and see that Item and Cart_Items information is also getting saved, and another to save Item data and check that corresponding Cart and Cart_Items are saved.

HibernateManyToManyMain.java

package com.journaldev.hibernate.main;

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

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

import com.journaldev.hibernate.model.Cart;
import com.journaldev.hibernate.model.Item;
import com.journaldev.hibernate.util.HibernateUtil;

public class HibernateManyToManyMain {
	
	//Saving many-to-many where Cart is primary
	public static void main(String[] args) {
		
		Item iphone = new Item();
		iphone.setPrice(100); iphone.setDescription("iPhone");
		
		Item ipod = new Item();
		ipod.setPrice(50); ipod.setDescription("iPod");
		
		Set<Item> items = new HashSet<Item>();
		items.add(iphone); items.add(ipod);
		
		Cart cart = new Cart();
		cart.setItems(items);
		cart.setTotal(150);
		
		Cart cart1 = new Cart();
		Set<Item> items1 = new HashSet<Item>();
		items1.add(iphone);
		cart1.setItems(items1);
		cart1.setTotal(100);
		
		SessionFactory sessionFactory = null;
		try{
		sessionFactory = HibernateUtil.getSessionFactory();
		Session session = sessionFactory.getCurrentSession();
		Transaction tx = session.beginTransaction();
		session.save(cart);
		session.save(cart1);
		System.out.println("Before committing transaction");
		tx.commit();
		sessionFactory.close();
		
		System.out.println("Cart ID="+cart.getId());
		System.out.println("Cart1 ID="+cart1.getId());
		System.out.println("Item1 ID="+iphone.getId());
		System.out.println("Item2 ID="+ipod.getId());
		
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			if(sessionFactory != null && !sessionFactory.isClosed()) sessionFactory.close();
		}
		
	}

}

When we execute the above Hibernate many-to-many mapping example program, we get the following output:

Hibernate Configuration loaded
Hibernate serviceRegistry created
Hibernate: insert into CART (cart_total) values (?)
Hibernate: insert into ITEM (item_desc, item_price) values (?, ?)
Hibernate: insert into ITEM (item_desc, item_price) values (?, ?)
Hibernate: insert into CART (cart_total) values (?)
Before committing transaction
Hibernate: insert into CART_ITEMS (cart_id, item_id) values (?, ?)
Hibernate: insert into CART_ITEMS (cart_id, item_id) values (?, ?)
Hibernate: insert into CART_ITEMS (cart_id, item_id) values (?, ?)
Cart ID=1
Cart1 ID=2
Item1 ID=1
Item2 ID=2

Note that once the Item data is saved through the first Cart, the item_id is generated, and while saving the second Cart, it’s not saved again. Another important point to note is that many-to-many join table data is getting saved when we are committing the transaction. It’s done for better performance in case we choose to roll back the transaction.

HibernateBiDirectionalManyToManyMain.java

package com.journaldev.hibernate.main;

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

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

import com.journaldev.hibernate.model.Cart;
import com.journaldev.hibernate.model.Item;
import com.journaldev.hibernate.util.HibernateUtil;

public class HibernateBiDirectionalManyToManyMain {

	//Saving many-to-many where Item is primary
	public static void main(String[] args) {
		
		Item iphone = new Item();
		iphone.setPrice(100); iphone.setDescription("iPhone");
		
		Item ipod = new Item();
		ipod.setPrice(50); ipod.setDescription("iPod");
		
		Cart cart = new Cart();
		cart.setTotal(150);
		
		Cart cart1 = new Cart();
		cart1.setTotal(100);
		
		Set<Cart> cartSet = new HashSet<Cart>();
		cartSet.add(cart);cartSet.add(cart1);
		
		Set<Cart> cartSet1 = new HashSet<Cart>();
		cartSet1.add(cart);
		
		iphone.setCarts(cartSet1);
		ipod.setCarts(cartSet);
		
		SessionFactory sessionFactory = null;
		try{
		sessionFactory = HibernateUtil.getSessionFactory();
		Session session = sessionFactory.getCurrentSession();
		Transaction tx = session.beginTransaction();
		session.save(iphone);
		session.save(ipod);
		tx.commit();
		sessionFactory.close();
		
		System.out.println("Cart ID="+cart.getId());
		System.out.println("Cart1 ID="+cart1.getId());
		System.out.println("Item1 ID="+iphone.getId());
		System.out.println("Item2 ID="+ipod.getId());
		
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			if(sessionFactory != null && !sessionFactory.isClosed()) sessionFactory.close();
		}
		
	}

}

Output of the above program is:

Hibernate Configuration loaded
Hibernate serviceRegistry created
Hibernate: insert into ITEM (item_desc, item_price) values (?, ?)
Hibernate: insert into CART (cart_total) values (?)
Hibernate: insert into ITEM (item_desc, item_price) values (?, ?)
Hibernate: insert into CART (cart_total) values (?)
Hibernate: insert into CART_ITEMS (item_id, cart_id) values (?, ?)
Hibernate: insert into CART_ITEMS (item_id, cart_id) values (?, ?)
Hibernate: insert into CART_ITEMS (item_id, cart_id) values (?, ?)
Cart ID=3
Cart1 ID=4
Item1 ID=3
Item2 ID=4

You can easily relate it to the earlier test program. Since we have configured bi-directional mapping, we can save Item or Cart, and the mapped data will get saved automatically.

Hibernate Many to Many Mapping Annotation

Now that we have seen how to configure many-to-many mapping using Hibernate XML configurations, let’s see an example of implementing it through annotations. We will implement one-directional many-to-many mapping using JPA annotations.

Hibernate Configuration XML File

hibernate-annotation.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
		"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
		"https://hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
	<session-factory>
		<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
		<property name="hibernate.connection.password">pankaj123</property>
		<property name="hibernate.connection.url">jdbc:mysql://localhost/TestDB</property>
		<property name="hibernate.connection.username">pankaj</property>
		<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>

		<property name="hibernate.current_session_context_class">thread</property>
		<property name="hibernate.show_sql">true</property>

		<mapping class="com.journaldev.hibernate.model.Cart1" />
		<mapping class="com.journaldev.hibernate.model.Item1" />
	</session-factory>
</hibernate-configuration>

Hibernate SessionFactory Utility Class

Our utility class to create SessionFactory looks like below.

HibernateAnnotationUtil.java

package com.journaldev.hibernate.util;

import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;

public class HibernateAnnotationUtil {

	private static SessionFactory sessionFactory;
	
	private static SessionFactory buildSessionFactory() {
        try {
            // Create the SessionFactory from hibernate-annotation.cfg.xml
        	Configuration configuration = new Configuration();
        	configuration.configure("hibernate-annotation.cfg.xml");
        	System.out.println("Hibernate Annotation Configuration loaded");
        	
        	ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
        	System.out.println("Hibernate Annotation serviceRegistry created");
        	
        	SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
        	
            return sessionFactory;
        }
        catch (Throwable ex) {
            System.err.println("Initial SessionFactory creation failed." + ex);
            ex.printStackTrace();
            throw new ExceptionInInitializerError(ex);
        }
    }
	
	public static SessionFactory getSessionFactory() {
		if(sessionFactory == null) sessionFactory = buildSessionFactory();
        return sessionFactory;
    }
}

Hibernate Many to Many Mapping Annotation Model Classes

This is the most important part for annotation-based mapping. Let’s first look at the Item table model class and then we will look into the Cart table model class.

Item1.java

package com.journaldev.hibernate.model;

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

@Entity
@Table(name="ITEM")
public class Item1 {

	@Id
	@Column(name="item_id")
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private long id;
	
	@Column(name="item_price")
	private double price;
	
	@Column(name="item_desc")
	private String description;
	
// Getter Setter methods
}

Item1 class looks simple, there is no relational mapping here.

Cart1.java

package com.journaldev.hibernate.model;

import java.util.Set;

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

@Entity
@Table(name = "CART")
public class Cart1 {

	@Id
	@Column(name = "cart_id")
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private long id;

	@Column(name = "cart_total")
	private double total;

	@ManyToMany(targetEntity = Item1.class, cascade = { CascadeType.ALL })
	@JoinTable(name = "CART_ITEMS", 
				joinColumns = { @JoinColumn(name = "cart_id") }, 
				inverseJoinColumns = { @JoinColumn(name = "item_id") })
	private Set<Item1> items;

//Getter Setter methods
}

The most important part here is the use of the @ManyToMany annotation and @JoinTable annotation, where we provide the table name and columns to be used for many-to-many mapping.

Hibernate Many to Many Annotation Mapping Test Program

Here is a simple test program for our Hibernate many-to-many mapping annotation-based configuration.

HibernateManyToManyAnnotationMain.java

package com.journaldev.hibernate.main;

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

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

import com.journaldev.hibernate.model.Cart1;
import com.journaldev.hibernate.model.Item1;
import com.journaldev.hibernate.util.HibernateAnnotationUtil;

public class HibernateManyToManyAnnotationMain {

	public static void main(String[] args) {
		Item1 item1 = new Item1();
		item1.setDescription("samsung"); item1.setPrice(300);
		Item1 item2 = new Item1();
		item2.setDescription("nokia"); item2.setPrice(200);
		Cart1 cart = new Cart1();
		cart.setTotal(500);
		Set<Item1> items = new HashSet<Item1>();
		items.add(item1); items.add(item2);
		cart.setItems(items);
		
		SessionFactory sessionFactory = null;
		try{
		sessionFactory = HibernateAnnotationUtil.getSessionFactory();
		Session session = sessionFactory.getCurrentSession();
		Transaction tx = session.beginTransaction();
		session.save(cart);
		System.out.println("Before committing transaction");
		tx.commit();
		sessionFactory.close();
		
		System.out.println("Cart ID="+cart.getId());
		System.out.println("Item1 ID="+item1.getId());
		System.out.println("Item2 ID="+item2.getId());
		
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			if(sessionFactory != null && !sessionFactory.isClosed()) sessionFactory.close();
		}
	}

}

When we execute the above program, it produces the following output:

Hibernate Annotation Configuration loaded
Hibernate Annotation serviceRegistry created
Hibernate: insert into CART (cart_total) values (?)
Hibernate: insert into ITEM (item_desc, item_price) values (?, ?)
Hibernate: insert into ITEM (item_desc, item_price) values (?, ?)
Before committing transaction
Hibernate: insert into CART_ITEMS (cart_id, item_id) values (?, ?)
Hibernate: insert into CART_ITEMS (cart_id, item_id) values (?, ?)
Cart ID=5
Item1 ID=6
Item2 ID=5

It’s clear that saving Cart is also saving data into Item and Cart_Items table. If you save only Item information, you will notice that Cart and Cart_Items data is not getting saved.

Conclusion

Hibernate Many-to-Many Mapping provides a powerful way to manage complex relationships in relational databases. Whether you use XML configurations or annotations, Hibernate simplifies the process of handling bidirectional or unidirectional mappings efficiently. By leveraging tools like @ManyToMany and @JoinTable, developers can streamline data persistence and ensure integrity across related entities. Choosing between XML and annotations depends on project requirements and personal preference, but both methods offer robust solutions for managing many-to-many relationships. As demonstrated, Hibernate allows flexibility and scalability for advanced ORM configurations, making it a preferred choice for enterprise applications.

Create a Free Account

Register now and get access to our Cloud Services.

Posts you might be interested in:

centron Managed Cloud Hosting in Deutschland

How to Calculate BLEU Score in Python?

Python
How to Calculate BLEU Score in Python? BLEU score in Python is a metric that measures the goodness of Machine Translation models. Though originally it was designed for only translation…