Hibernate – Many-to-many relationship example
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.
- Java Core Technology - Java RegEx, Java XML, Java I/O, Java Misc
- J2EE Frameworks - Hibernate, Spring 2.5, Spring MVC, Struts 1.x, Struts 2.x
- Build Tools - Maven, Archiva
- Unit Test - jUnit, TestNG
- Client Scripts - jQuery
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.
[...] Many-to-many relationship example A many-to-many relationship project demonstration. [...]
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.