Many-to-many relationships occur when each record in an entity may have many linked records in another entity and vice-versa.
“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.
Hibernate implementation
In Hibernate, this “many-to-many” relationship can implemented in two ways :
- Hibernate XML Mapping file
- Hibernate Annotation
1. Hibernate XML mapping file
In XML mapping way, you need three Java classes to hold the table data,
- Stock.java -> STOCK table
- Category.java -> CATEGORY table
- StockCategory.java -> STOCK_CATEGORY table
and three XML mapping files to describe the table relationship.
- Stock.hbm.xml -> STOCK table
- Category.hbm.xml -> CATEGORY table
- StockCategory.hbm.xml -> STOCK_CATEGORY table
Stock.java
public class Stock implements java.io.Serializable { private Set<StockCategory> stockCategories = new HashSet<StockCategory>(0); ... public Set<StockCategory> getStockCategories() { return this.stockCategories; } public void setStockCategories(Set<StockCategory> stockCategories) { this.stockCategories = stockCategories; }
Category.java
public class Category implements java.io.Serializable { private Set<StockCategory> stockCategories = new HashSet<StockCategory>(0); ... public Set<StockCategory> getStockCategories() { return this.stockCategories; } public void setStockCategories(Set<StockCategory> stockCategories) { this.stockCategories = stockCategories; }
StockCategory.java
public class StockCategory implements java.io.Serializable { private Integer stockCategoryId; private Stock stock; private Category category; ...
Stock.hbm.xml
... <class name="com.mkyong.common.Stock" table="stock" ...> <id name="stockId" type="java.lang.Integer"> <column name="STOCK_ID" /> <generator class="identity" /> </id> ... <set name="stockCategories" inverse="true" lazy="true" table="stock_category" ...> <key> <column name="STOCK_ID" not-null="true" /> </key> <one-to-many class="com.mkyong.common.StockCategory" /> </set> ...
Set inverse=”true” in set variable, Stock does not want to maintain the relationship.
Category.hbm.xml
... <class name="com.mkyong.common.Category" table="category" ...> <id name="categoryId" type="java.lang.Integer"> <column name="CATEGORY_ID" /> <generator class="identity" /> </id> ... <set name="stockCategories" inverse="true" lazy="true" table="stock_category" ...> <key> <column name="CATEGORY_ID" not-null="true" /> </key> <one-to-many class="com.mkyong.common.StockCategory" /> </set> ...
Set inverse=”true” in set variable, Category does not want to maintain the relationship also.
StockCategory.hbm.xml
... <class name="com.mkyong.common.StockCategory" table="stock_category" ...> <id name="stockCategoryId" type="java.lang.Integer"> <column name="STOCK_CATEGORY_ID" /> <generator class="identity" /> </id> <many-to-one name="stock" class="com.mkyong.common.Stock" ...> <column name="STOCK_ID" not-null="true" /> </many-to-one> <many-to-one name="category" class="com.mkyong.common.Category" ...> <column name="CATEGORY_ID" not-null="true" /> </many-to-one> </class> ...
StockCategory maintain the relationship between Stock and Category, see inverse=”true” explanation.
2. Hibernate annotation
In annotation way, three classes are required, all the relationships are declared inside the Java classes.
- Stock.java -> STOCK table
- category.java -> CATEGORY table
- StockCategory.java -> STOCK_CATEGORY table
Stock.java
... @Entity @Table(name = "stock", catalog = "mkyong", uniqueConstraints = { @UniqueConstraint(columnNames = "STOCK_NAME"), @UniqueConstraint(columnNames = "STOCK_CODE") }) public class Stock implements java.io.Serializable { private Set<StockCategory> stockCategories = new HashSet<StockCategory>(0); ... @Id @GeneratedValue(strategy = IDENTITY) @Column(name = "STOCK_ID", unique = true, nullable = false) public Integer getStockId() { return this.stockId; } ... @OneToMany(fetch = FetchType.LAZY, mappedBy = "stock") public Set<StockCategory> getStockCategories() { return this.stockCategories; } public void setStockCategories(Set<StockCategory> stockCategories) { this.stockCategories = stockCategories; } ...
Category.java
... @Entity @Table(name = "category", catalog = "mkyong") public class Category implements java.io.Serializable { private Set<StockCategory> stockCategories = new HashSet<StockCategory>(0); ... @Id @GeneratedValue(strategy = IDENTITY) @Column(name = "CATEGORY_ID", unique = true, nullable = false) public Integer getCategoryId() { return this.categoryId; } ... @OneToMany(fetch = FetchType.LAZY, mappedBy = "category") public Set<StockCategory> getStockCategories() { return this.stockCategories; } public void setStockCategories(Set<StockCategory> stockCategories) { this.stockCategories = stockCategories; } ...
StockCategory.java
@Entity @Table(name = "stock_category", catalog = "mkyong", uniqueConstraints = @UniqueConstraint(columnNames = { "STOCK_ID", "CATEGORY_ID" })) public class StockCategory implements java.io.Serializable { private Integer stockCategoryId; private Stock stock; private Category category; ... @Id @GeneratedValue(strategy = IDENTITY) @Column(name = "STOCK_CATEGORY_ID", unique = true, nullable = false) public Integer getStockCategoryId() { return this.stockCategoryId; } @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "STOCK_ID", nullable = false) public Stock getStock() { return this.stock; } @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "CATEGORY_ID", nullable = false) public Category getCategory() { return this.category; } ...
The code is self-explanatory, all the @annotation are the XML mapping tag substitution.
Test It
Run the example, both annotation and XML mapping file will generate the same output. Hibernate inserts a row into the STOCK table, a row into the CATEGORY table, and both relationship in STOCK_CATEGORY table.
Stock stock = new Stock(); Category category = new Category(); ... session.save(stock); session.save(category); ... StockCategory stockCategory = new StockCategory(stock, category); session.save(stockCategory);
Output
Hibernate:
insert into mkyong.stock
(STOCK_CODE, STOCK_NAME)
values (?, ?)
Hibernate:
insert into mkyong.category
(`DESC`, NAME)
values (?, ?)
Hibernate:
insert into mkyong.stock_category
(CATEGORY_ID, STOCK_ID)
values (?, ?)Download Full Project
In this tutorial, many files and configuration steps are hidden, only the important files are remark. If you are interest to do the hand-on, you can download the full project here.
- Hibernate many-to-many XML mapping example – Full project download.
- Hibernate many-to-many annotation example – Full project download.
These two projects are developed in Eclipse IDE, using Maven as project management tool, and MySQL as database engine. The MySQL table creation scripts are stored in “db” folder.


