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).
For many to many with extra columns in join table, please refer to this tutorial.
Tools and technologies used in this tutorials :
- Hibernate 3.6.3.Final
- MySQL 5.1.15
- Maven 3.0.3
- Eclipse 3.6
Project Structure
Project structure of this tutorial.
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.
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
For many-to-many in Hibernate annotation, please refer to this example.
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.
: thanks
Thank you, Nandu.
you saved my life 😀
Thanks, nice post
Nice examle. I have one doubt here. If I want to insert the data into stock_category table how to do it?
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 ?
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?
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
please tell me how to inject repository class in converter class
As always, these examples are very helpful. Thanks!
Mitch
In this case, Stock is the owner of the relationship. What are the changes to be made to make Category as owner?
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!
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….
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?
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
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:
This will ensure that the insertion will work properly in all 3 tables
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
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
Thanks for sharing useful article. I don’t need one-to-many and many-to-one anymore.
don’t need? why said so?
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
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]
The faster way is use Hibernate tools to generate your mapping file automatically. Refer to following two articles.
https://mkyong.com/hibernate/how-to-install-hibernate-tools-in-eclipse-ide/
https://mkyong.com/hibernate/how-to-generate-code-with-hibernate-tools/
Can you please explain the mapping what it means , like
what does this means , what is what it refers to ,
the project download is missing a lot of dependent jar files.
Maven-based project, use maven to download it.
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.
This example is not something for the sake of providing example. The link table is a common RDBMS table design pattern for M-M relationship.
For hibernate, there are many ways to implement M-M table design, and the link table class is just one of the way, i will provide more ways in coming future.
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.
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/
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.
if I have an attribute in the table StockCategory date???
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!
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.
As i know, there are many ways to implement the many to many relationship, all lead to Rome.
Btw, thanks for sharing your thought.
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.
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?
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
oh.. you can download the full sample at the end of the article, both in xml mapping and annotation ways.
Would you mind to tell me which “way” or “word” i misleading the developer? and what wrong with this tutorial? In database, many to many is always involve third table for the relationship. Look forward your idea and comment.
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
Hi Rakesh, thanks for your inputs.
For large table , primary key is a must 🙂 , class for “stockCategory” is just another way to achieve the same thing.
Hi mkyong, I was just commented on the example you gave. Anyways nice good article.