Main Tutorials

Hibernate – Many-to-Many example (XML Mapping)

Many-to-many relationships occur when each record in an entity may have many linked records in another entity and vice-versa.

In this tutorial, we show you how to work with many-to-many table relationship in Hibernate, via XML mapping file (hbm).

Note
For many to many with extra columns in join table, please refer to this tutorial.

Tools and technologies used in this tutorials :

  1. Hibernate 3.6.3.Final
  2. MySQL 5.1.15
  3. Maven 3.0.3
  4. Eclipse 3.6

Project Structure

Project structure of this tutorial.

many to many project folder

Project Dependency

Get latest hibernate.jar from JBoss repository.

File : pom.xml


<project ...>
 
	<repositories>
		<repository>
			<id>JBoss repository</id>
			<url>http://repository.jboss.org/nexus/content/groups/public/</url>
		</repository>
	</repositories>
 
	<dependencies>
 
		<!-- MySQL database driver -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.15</version>
		</dependency>
 
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>3.6.3.Final</version>
		</dependency>
 
		<dependency>
			<groupId>javassist</groupId>
			<artifactId>javassist</artifactId>
			<version>3.12.1.GA</version>
		</dependency>
 
	</dependencies>
</project>

1. “Many-to-many” example

This is a many-to-many relationship table design, a STOCK table has more than one CATEGORY, and CATEGORY can belong to more than one STOCK, the relationship is linked with a third table called STOCK_CATEGORY.

Table STOCK_CATEGORY only consist of two primary keys, and also foreign key reference back to STOCK and CATEGORY.

many to many ER diagram

MySQL table scripts


CREATE TABLE `stock` (
  `STOCK_ID` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `STOCK_CODE` varchar(10) NOT NULL,
  `STOCK_NAME` varchar(20) NOT NULL,
  PRIMARY KEY (`STOCK_ID`) USING BTREE,
  UNIQUE KEY `UNI_STOCK_NAME` (`STOCK_NAME`),
  UNIQUE KEY `UNI_STOCK_ID` (`STOCK_CODE`) USING BTREE
)

CREATE TABLE `category` (
  `CATEGORY_ID` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `NAME` varchar(10) NOT NULL,
  `DESC` varchar(255) NOT NULL,
  PRIMARY KEY (`CATEGORY_ID`) USING BTREE
)

CREATE TABLE  `stock_category` (
  `STOCK_ID` int(10) unsigned NOT NULL,
  `CATEGORY_ID` int(10) unsigned NOT NULL,
  PRIMARY KEY (`STOCK_ID`,`CATEGORY_ID`),
  CONSTRAINT `FK_CATEGORY_ID` FOREIGN KEY (`CATEGORY_ID`) REFERENCES `category` (`CATEGORY_ID`),
  CONSTRAINT `FK_STOCK_ID` FOREIGN KEY (`STOCK_ID`) REFERENCES `stock` (`STOCK_ID`)
)

2. Hibernate Model Class

Create two model classes – Stock.java and Category.java, to represent the above tables. No need to create an extra class for table ‘stock_category‘.

File : Stock.java


package com.mkyong.stock;

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

public class Stock implements java.io.Serializable {

	private Integer stockId;
	private String stockCode;
	private String stockName;
	private Set<Category> categories = new HashSet<Category>(0);

	//getter, setter and constructor
}

File : Category.java


package com.mkyong.stock;

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

public class Category implements java.io.Serializable {

	private Integer categoryId;
	private String name;
	private String desc;
	private Set<Stock> stocks = new HashSet<Stock>(0);

	//getter, setter and constructor
}

3. Hibernate XML Mapping

Now, create two Hibernate mapping files (hbm) – Stock.hbm.xml and Category.hbm.xml. You will noticed the third ‘stock_category‘ table is reference via “many-to-many” tag.

File : Stock.hbm.xml


<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.mkyong.stock.Stock" table="stock" catalog="mkyongdb">
        <id name="stockId" type="java.lang.Integer">
            <column name="STOCK_ID" />
            <generator class="identity" />
        </id>
        <property name="stockCode" type="string">
            <column name="STOCK_CODE" length="10" not-null="true" unique="true" />
        </property>
        <property name="stockName" type="string">
            <column name="STOCK_NAME" length="20" not-null="true" unique="true" />
        </property>
        <set name="categories" table="stock_category" 
        	inverse="false" lazy="true" fetch="select" cascade="all" >
            <key>
                <column name="STOCK_ID" not-null="true" />
            </key>
            <many-to-many entity-name="com.mkyong.stock.Category">
                <column name="CATEGORY_ID" not-null="true" />
            </many-to-many>
        </set>
    </class>
</hibernate-mapping>

File : Category.hbm.xml


<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.mkyong.stock.Category" table="category" catalog="mkyongdb">
        <id name="categoryId" type="java.lang.Integer">
            <column name="CATEGORY_ID" />
            <generator class="identity" />
        </id>
        <property name="name" type="string">
            <column name="NAME" length="10" not-null="true" />
        </property>
        <property name="desc" type="string">
            <column name="[DESC]" not-null="true" />
        </property>
        <set name="stocks" table="stock_category" inverse="true" lazy="true" fetch="select">
            <key>
                <column name="CATEGORY_ID" not-null="true" />
            </key>
            <many-to-many entity-name="com.mkyong.stock.Stock">
                <column name="STOCK_ID" not-null="true" />
            </many-to-many>
        </set>
    </class>
</hibernate-mapping>

4. Hibernate Configuration File

Now, puts Stock.hbm.xml and Category.hbm.xml and MySQL detail in hibernate.cfg.xml.

File : hibernate.cfg.xml


<?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>
    <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mkyongdb</property>
    <property name="hibernate.connection.username">root</property>
    <property name="hibernate.connection.password">password</property>
    <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
    <property name="show_sql">true</property>
    <property name="format_sql">true</property>
    <mapping resource="com/mkyong/stock/Stock.hbm.xml" />
    <mapping resource="com/mkyong/stock/Category.hbm.xml" />
</session-factory>
</hibernate-configuration>

5. Run It

Run it, Hibernate will insert a record into the STOCK table, two records into the CATEGORY table, and also two records into the STOCK)CATEGORY table.

File : App.java


package com.mkyong;

import java.util.HashSet;
import java.util.Set;
import org.hibernate.Session;
import com.mkyong.stock.Category;
import com.mkyong.stock.Stock;
import com.mkyong.util.HibernateUtil;

public class App {
	public static void main(String[] args) {
		
        System.out.println("Hibernate many to many (XML Mapping)");
	Session session = HibernateUtil.getSessionFactory().openSession();

	session.beginTransaction();

	Stock stock = new Stock();
        stock.setStockCode("7052");
        stock.setStockName("PADINI");
 
        Category category1 = new Category("CONSUMER", "CONSUMER COMPANY");
        Category category2 = new Category("INVESTMENT", "INVESTMENT COMPANY");
    
        Set<Category> categories = new HashSet<Category>();
        categories.add(category1);
        categories.add(category2);
        
        stock.setCategories(categories);
        
        session.save(stock);
    
	session.getTransaction().commit();
	System.out.println("Done");
	}
}

Output …result should be self-explanatory


Hibernate many to many (XML Mapping)
Hibernate: 
    insert 
    into
        mkyongdb.stock
        (STOCK_CODE, STOCK_NAME) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        mkyongdb.category
        (NAME, `DESC`) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        mkyongdb.category
        (NAME, `DESC`) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        stock_category
        (STOCK_ID, CATEGORY_ID) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        stock_category
        (STOCK_ID, CATEGORY_ID) 
    values
        (?, ?)
Done
Hibernate Annotation
For many-to-many in Hibernate annotation, please refer to this example.

Reference

  1. Hibernate Documentation – many to many relationship.

About Author

author image
Founder of Mkyong.com, love Java and open source stuff. Follow him on Twitter. If you like my tutorials, consider make a donation to these charities.

Comments

Subscribe
Notify of
42 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments
Nandu K
7 years ago

for those who are getting ora-01747 error,
change column name from desc to something else as DESC is a reserved key word.
make changes accordingly in the hdm file too.

Jyothi thomas
7 years ago
Reply to  Nandu K

: thanks

Jishnu
7 years ago
Reply to  Nandu K

Thank you, Nandu.
you saved my life 😀

Binh Thanh Nguyen
8 years ago

Thanks, nice post

Ashish Mishra
8 years ago

Nice examle. I have one doubt here. If I want to insert the data into stock_category table how to do it?

facilus
9 years ago

Hey,

In my composite table i’ve another fields :

1- Composite primary key like this example (id_table1, id_table2)
2- a foreign key with table3 (id_table3)
3- a column with Int value (quantity)

Questions :

1- How to map this model in “hbm file” and in java ?
2- How to use criteria for obteining list with key = id_table_1 ?

Alessandra P.
9 years ago

I am using Oracle DB so I have to use a sequence ( ) instead of your because Oracle DB doesn’t support identity key generator.

The problem is that a sequence generates an id even when it is already assigned to a table row. What should I use instead?

gaurav shirbhate
10 years ago

I have to inject Repository class in Converter class which implements converter interface for accessing all the data from database, but i am not getting how to inject repository class in converter class

gaurav shirbhate
10 years ago

please tell me how to inject repository class in converter class

Mitch
11 years ago

As always, these examples are very helpful. Thanks!

Mitch

Ing
11 years ago

In this case, Stock is the owner of the relationship. What are the changes to be made to make Category as owner?

Keith
11 years ago

I know this is old, but just to say; I don’t know what this sunil is on about.

I have just found this example, used it to replace my (incorrect) implementation os a many-to-many relationship and it’s worked first time!!!!

Another quality and easy to understand example from MkYong!

IMO, the best site for coding examples, on the Net!

Sathish
11 years ago

Dear Sir,

Sir,while i’m using cascade=”save” to perform insertion operations in relationships,i’m getting an exception like “Unsupported cascading style:save”….what the problem would be???????? please help me out this….

tunnaruto
11 years ago

after following rhis tutorial, when i run an HQL query “from Stock” i got the following exception:
org.hibernate.MappingException: An association from the table stock_category refers to an unmapped class: pojosMkyong.Category
at org.hibernate.cfg.Configuration.secondPassCompileForeignKeys(Configuration.java:1252)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1170)
at org.hibernate.cfg.AnnotationConfiguration.secondPassCompile(AnnotationConfiguration.java:324)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1286)
at org.hibernate.cfg.AnnotationConfiguration.buildSessionFactory(AnnotationConfiguration.java:859)

why?

michael
11 years ago

I follow this tutorial from start to end but the database populates the stock and stock category table and not three tables. Why like this?

Thanks

Feri
11 years ago
Reply to  michael

Dear Michael,
The solution to your problem could be the following: Just try to set the following property (inverse) from this line to false in both hbm files:

 
<set name="stocks" table="stock_category" inverse="true" lazy="true" fetch="select">

This will ensure that the insertion will work properly in all 3 tables

aminos
12 years ago

Hey thanks for the tutorial can u make the same example but with extra columns the stock_category table ,i need it with xml not annotation please help me

Pramod
12 years ago

how to i use struts with hibernate for the above example where i need to have more than 1 actionform bean. So that the data can b collected for different table and inserted on the go.

Example: I have a employeedtls table ,addrstable,logindtls table where employee is the master table whenever a new employee joins the house his details need to be inserted in to 3 tables first in logindtls and get primary key then from addrsdtls and get the primary key at last we need to insert the collected primary keys into employeedtls as a foreign key and that i need to do using struts action in a single web page.

Thank you
please reply

Quang
12 years ago

Thanks for sharing useful article. I don’t need one-to-many and many-to-one anymore.

Harsha S
12 years ago

hey hi all harsha here
i do hv small problem with hibernate, its related to the topic under discussion.
could u please tell me

in Stocks , category , Stock_Category tables
for me category remains constant, i don’t wanna change it (means i do have constant category table)
so if i create new stocks and add existing categories to it what to do

if i try the above its quite works but Category table is unnecessarily getting updated even tho new category is not getting inserted……
i don’t wan any changes to be made on category table.
help me with this

means i need little bit of help in hibernate mapping file

Suhas Gunda
11 years ago
Reply to  Harsha S

Hi Harsha,

did you find answer of above question?? I am facing same problem.. If you have answer then please let me know on [email protected]

Arun
12 years ago

Can you please explain the mapping what it means , like

what does this means , what is what it refers to ,

nnad
12 years ago

the project download is missing a lot of dependent jar files.

Arby
13 years ago

MYkong, I love your website. But in this example I’m thinking you simply did something just for the sake of providing an example.

IT does not seem practical to have Category store Set where each StockCategory has the (parent) Category repeating in it. Not withstanding the fact that M-M relationship can be broken into 1-M and M-1 relationship, even practically that would seem to be the intent. Stock needs to know all its Categories and Category needs to know all Stocks in it.

Finally, and this may be Hibernate thingy, I refuse to accept understand the logic behind making a Class to represent a “Link Table” for the M-M relationship. My domain classes are Stock and Category. They mirror analysis of my Business and correspond to STOCK and CATEGORY Entities aka “Tables in RDBMS”. The “link table” is NOT a Business Entity and having a class to represent it just plain bad design.

Just my two cents.

Arby
13 years ago
Reply to  mkyong

I understand this might be Hibernate specific way. But that’s precisely my problem. You make a tongue in cheek remark in your essay on how you convinced your management to use Hibernate. You cite there is lot of demand for Hibernate as one of the reasons 🙂

My problem is my Domain and Architectural decisions should not be held hostage to tool I’m using for data persistence. Creating a domain class for representing a link table IMHO is not good design because the Link Class has no parallel representation in my Business Domain. The Link Class is simply adulterating my Java Domain.

I will look forward to your example of an alternative which does not make it necessary to create a Link Class.

Rakesh
13 years ago

Dear All ,

I have just gone through one site, and found the explantion with simple example of Many to Many relation in hibernate using JPA .

http://j2eereference.com/2011/01/many-to-many-mapping-in-hibernate/

SQL tutorial
13 years ago

I am taking up the subject java programming SE. I will try this and maybe if I don’t understand some of the codes, I can ask my professor about it.

carldeltoro
13 years ago

if I have an attribute in the table StockCategory date???

Hassan
13 years ago

Actually M-M relationship can be decomposed in to two 1-M relationships, but I do not need this extra effort unless I’ve extra columns in the mapper table that I really need(e.g. date of stock_category).

In M-M , read operations work fine, but save/delete/update is not quite easy!

Turion
13 years ago

Normally, a many to many relationship is realized by using the @ManyToMany annotation. So let’s say you have users and roles and each user can have a number of roles, while a certain role can belong to a number of users. Then you would use something like this:

Class User {

@ManyToMany(targetEntity = Role.class)
@JoinTable(name = “user_has_role”,
joinColumns = @JoinColumn(name = “userId”),
inverseJoinColumns = @JoinColumn(name = “roleId”)
)
public Set getRoles() {
return roles;
}

}

Of course you need a third table to map this kind of relationship, but I dont’t understand why you are using the @ManyToOne annotation.

Gaurav
13 years ago

Thanks for this tutorial,
I am wondering, if it possible, if in this case STOCK_CATEGORY table will be having composite primary key with
CATEGORY_ID and STOCK_ID i.e. no other PK in this case.
Then How will we define many to many relationship.
I am struggling to resolve this scenario.

gary
13 years ago

hi,

Found the tutorial quite useful. Thanks

please can you also post hibernate many-many CRUD application? For example, Instead of adding a new category, I want to reference existing category in my Stock class, all I need to do is to insert Stock table and into the mapping table.

can you help pls?

sunil
13 years ago

You are completely wrong regarding mapping of many-to-many. I dont see anything like many-to-many. You are just trying to give alternate way but not exact code or tutorial. Please verify it since it is misleading the developer….
Thanks
sunil

Rakesh
13 years ago
Reply to  mkyong

Dear mkyong,

You are absolutly right .We need a third table to connect the two tables which we need to establish the relation. This is the way to implement the Many to Many relation using hibernate and JPA.

Actually speaking, third table doesnt need a primary key and we dont need to create a clas for the table stockCategory and ManyTone is not really required.

please go throught this article .

http://j2eereference.com/2011/01/many-to-many-mapping-in-hibernate/

Regards,
Rakesh.B.K

Rakesh
13 years ago
Reply to  mkyong

Hi mkyong, I was just commented on the example you gave. Anyways nice good article.