Hibernate – Many-to-Many example (Annotation)

In this tutorial, it will reuse the entire infrastructure of the previous “Hibernate many to many example – XML mapping” tutorial, enhance it to support Hibernare / JPA annotation.

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

Project Structure

Review the new project structure of this tutorial.

many to many project folder

1. “Many-to-many” table relationship

See the previous many to many table relationship again.

many to many ER diagram

2. Hibernate Model Class

Update previous model classes – Stock.java and Category.java, and define new annotation code inside.

File : Stock.java


package com.mkyong.stock;

import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.CascadeType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

@Entity
@Table(name = "stock", catalog = "mkyongdb", uniqueConstraints = {
		@UniqueConstraint(columnNames = "STOCK_NAME"),
		@UniqueConstraint(columnNames = "STOCK_CODE") })
public class Stock implements java.io.Serializable {

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

	public Stock() {
	}

	public Stock(String stockCode, String stockName) {
		this.stockCode = stockCode;
		this.stockName = stockName;
	}

	public Stock(String stockCode, String stockName, Set<Category> categories) {
		this.stockCode = stockCode;
		this.stockName = stockName;
		this.categories = categories;
	}

	@Id
	@GeneratedValue(strategy = IDENTITY)
	@Column(name = "STOCK_ID", unique = true, nullable = false)
	public Integer getStockId() {
		return this.stockId;
	}

	public void setStockId(Integer stockId) {
		this.stockId = stockId;
	}

	@Column(name = "STOCK_CODE", unique = true, nullable = false, length = 10)
	public String getStockCode() {
		return this.stockCode;
	}

	public void setStockCode(String stockCode) {
		this.stockCode = stockCode;
	}

	@Column(name = "STOCK_NAME", unique = true, nullable = false, length = 20)
	public String getStockName() {
		return this.stockName;
	}

	public void setStockName(String stockName) {
		this.stockName = stockName;
	}

	@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
	@JoinTable(name = "stock_category", catalog = "mkyongdb", joinColumns = { 
			@JoinColumn(name = "STOCK_ID", nullable = false, updatable = false) }, 
			inverseJoinColumns = { @JoinColumn(name = "CATEGORY_ID", 
					nullable = false, updatable = false) })
	public Set<Category> getCategories() {
		return this.categories;
	}

	public void setCategories(Set<Category> categories) {
		this.categories = categories;
	}

}

File : Category.java


package com.mkyong.stock;

import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

@Entity
@Table(name = "category", catalog = "mkyongdb")
public class Category implements java.io.Serializable {

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

	public Category() {
	}

	public Category(String name, String desc) {
		this.name = name;
		this.desc = desc;
	}

	public Category(String name, String desc, Set<Stock> stocks) {
		this.name = name;
		this.desc = desc;
		this.stocks = stocks;
	}

	@Id
	@GeneratedValue(strategy = IDENTITY)
	@Column(name = "CATEGORY_ID", unique = true, nullable = false)
	public Integer getCategoryId() {
		return this.categoryId;
	}

	public void setCategoryId(Integer categoryId) {
		this.categoryId = categoryId;
	}

	@Column(name = "NAME", nullable = false, length = 10)
	public String getName() {
		return this.name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Column(name = "[DESC]", nullable = false)
	public String getDesc() {
		return this.desc;
	}

	public void setDesc(String desc) {
		this.desc = desc;
	}

	@ManyToMany(fetch = FetchType.LAZY, mappedBy = "categories")
	public Set<Stock> getStocks() {
		return this.stocks;
	}

	public void setStocks(Set<Stock> stocks) {
		this.stocks = stocks;
	}

}

3. Hibernate Configuration File

Puts annotated classes Stock.java and Category.java in hibernate.cfg.xml like this :

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>
    <mapping class="com.mkyong.stock.Stock" />
    <mapping class="com.mkyong.stock.Category" />
</session-factory>
</hibernate-configuration>

4. Run It

Run it, the result is self-explanatory.

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 (Annotation)");
	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


Hibernate many to many (Annotation)
Hibernate: 
    insert 
    into
        mkyongdb.stock
        (STOCK_CODE, STOCK_NAME) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        mkyongdb.category
        (`DESC`, NAME) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        mkyongdb.category
        (`DESC`, NAME) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        mkyongdb.stock_category
        (STOCK_ID, CATEGORY_ID) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        mkyongdb.stock_category
        (STOCK_ID, CATEGORY_ID) 
    values
        (?, ?)
Done

Reference

  1. Hibernate Documentation – many to many relationship.

About the Author

author image
mkyong
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

avatar
32 Comment threads
11 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
34 Comment authors
Przemys?aw G?sieniecNguy?n Qu?c MinhSaraVo Duy Khanhkuldeep yadav Recent comment authors
newest oldest most voted
Raul Ricardo Mendoza
Guest
Raul Ricardo Mendoza

I have a problem with this this is the error

Could not determine type for: java.util.List, at table: categories, for columns: [org.hibernate.mapping.Column(stocks)]

I have the same code that you have but I don’t know what is missing

Best Regards

Sara
Guest
Sara

same problem here…

Przemys?aw G?sieniec
Guest
Przemys?aw G?sieniec

It has been a while. The problem is because List was not instantiated as any concrete type, for example ArrayList() or LinkedList(). However now in the example, it is created as a Set and indeed it is instantiated as HashSet();

Oleg Poltoratskii
Guest
Oleg Poltoratskii

OMG! 10 banner on page!

Vo Duy Khanh
Guest
Vo Duy Khanh

Thank you verry much, this topic very helpful for me.

Mike
Guest
Mike

Why is that the @JoinTable on the Stock side? Can I do the reverse:

Category:

@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinTable(name = “stock_category”, catalog = “mkyongdb”, joinColumns = { @JoinColumn(name = “CATEGORY_ID”, nullable = false, updatable = false) }, inverseJoinColumns = { @JoinColumn(name = “STOCK_ID”, nullable = false, updatable = false) })
public Set getStocks() {
return this.stocks;
}

Stock:

@ManyToMany(fetch = FetchType.LAZY, mappedBy = “stocks”)

public Set getCategories() {
return this.categories;
}

Savani
Guest
Savani

I think, you should also be providing details how to create table schema details?

Richard
Guest
Richard

Works like a charm, you’re the best !

Marcos Cruz
Guest
Marcos Cruz

Hi,
I decided to change from Collection to Set in my project, but I am getting NullPointerException after I load the entity with the fetched Set. And it works with Collection. Do you have idea how can I solve this? Thanks in advance.

Manoj Sawant
Guest
Manoj Sawant

Please add import statements in model classes to import other model class,….because in many-to-many relation it will be easy to get things clear by looking at import statements in model classes.

Captain Kay
Guest
Captain Kay

A hibernate syntax question, if you want to show all stocks or categories what would be the hibernate query?

ranjan
Guest
ranjan

HI Mykong,
Can you please explain me How it is working as many to many association.Because it one stock can have many category.But i cannot do vice versa.

lethal.industry
Guest
lethal.industry

i implemented this solution for a form with checkboxes of a many-to-many relationship.
It’s not not the best, but it works… i post this so maybe someone can find it usefull:

http://stackoverflow.com/questions/24673798/spring-mvc-hibernate-a-form-with-checkboxes-for-manytomany-relationship/24701007#24701007

lethal.industry
Guest
lethal.industry

i always get “No converter found capable of converting from type java.lang.String to type @javax.persistence.OneToMany java.util.Set”

where can i find an example / tutorial for a form with checboxes of a many-to-many relationship?

Sergey
Guest
Sergey

It is clear how to annotate two tables with one join tabke, and how to annotate three tables with one join table?
For example: Students, Groups, Subjects with one join table Student_Group_Subject

Giga
Guest
Giga

I wrote my code like it is in example and I got error ” org.hibernate.hql.internal.ast.QuerySyntaxException: AcadProgramSubjectWithJoin is not mapped [from AcadProgramSubjectWithJoin where programId = ?1 and isActive = ?2] ” . Can anyone help me ?

HeYou
Guest
HeYou
Anup Mehta
Guest
Anup Mehta

What does inverseJoinColumns annotation does in STOCK table ?
Is it compulsary to have primary key foreign key relationship between Stock,Stock_catgory and Category table? Please reply.

chiranjeevi
Guest
chiranjeevi

Can we achieve OrderOrderItem Item relationship using hibernate mamy-to-many relation mapping. If possible can you please suggest me the approach.

for above three table structure please find following link
http://managersforum.com/eLearning/Graphics/OrderDB.gif


chiru

Tomasz Drozdowski
Guest
Tomasz Drozdowski

Hello mkyong.

Is there any benefits of using @UniqueConstrains on table level AND then setting property unique=true at column level?
Speciffication says:
“Use the @UniqueConstraint annotation to specify that a unique constraint is to be included in the generated DDL for a primary or secondary table. Alternatively, you can specify unique constraints at the column level (see @Column).”
So one might assume that you do not need both of them.

Lenin Alomoto
Guest
Lenin Alomoto

Thank excellent example.
I have a question.

I have three entities (stock, category and stock_category) and these are stored as shown in the example, but I want to store only two entities (stock and stock_category) that already has data category.

How to insert only two entities (stock and stock_category)?

Thanks again

Lenin Alomoto
Guest
Lenin Alomoto

Sorry for the inconvenience and found the solution to my problem:

Stock stock = new Stock();
stock.setStockCode(“7053”);
stock.setStockName(“OTHER PADINI”);
/* INICIO
Español: En esta seccion se emula una consulta a la base de datos y se recupera un registro de categoria.

English: In this section emulates a query to the database and retrieves a category record
*/
Category category1 = new Category();
category1.setCategoryId(1);
category1.setName(“CONSUMER”);
category1.setDesc(“CONSUMER COMPANY”);
/*
FIN de consulta
END query
*/

Set categories = new HashSet();
categories.add(category1);

stock.setCategories(categories);

session.save(stock);

Christian
Guest
Christian

Troubles when i merge the first entity!! I have two entities (Contract,Book) mapped with the same annotations @ManytoMany with a List instead a set, and the fetch of de firts entity (Contract) is eager because i have to show for one contract his BookList. I have one entity ContractBook mapped to the table contract_book but i dont use that for nothing the problem is when i edit (merge)the first entity (Contract) takes 1 minute to record. when i see the logs of hibernate i have next the query of table contract_book, repeates 18506 times (the number of record of de… Read more »

raspberry ketone pure
Guest
raspberry ketone pure

I used to be more than happy to seek out this web-site.I wanted to thanks on your time for this glorious learn!! I undoubtedly enjoying every little bit of it and I have you bookmarked to check out new stuff you weblog post.

Cristiane
Guest
Cristiane

How can I update the tables stock and category in the above example?
I have one project with same details and I am not able to update the two tables.

Usman Awan
Guest
Usman Awan

Hi Mkyong,

What if I have to do in case of deleting a single record from stock_category? How do I will delete the stock entry with the specific record ID?

Regards,
Usman

Derek Lee
Guest
Derek Lee

First, in real world the Category should be a domain object so you should use CascadeType.REFRESH instead of CascadeType.ALL.

Derek Lee
Guest
Derek Lee
Stock stock = em.find(Stock.class, 1);
Set<Category> categories = new HashSet<Category>();
stock.setCategories(categories);
em.merge(stock);
David
Guest
David

What if I dont want to save the categories?

Lets say categories is a domain table and it’s records are already saved. I just want to save stock and the association table (stock_category).

What should I do?

Derek Lee
Guest
Derek Lee

You need to change CascadeType.ALL to CascadeType.REFRESH.

Lenin Alomoto
Guest
Lenin Alomoto

Regards
Where change that?

Ahmet BAYIRLI
Guest
Ahmet BAYIRLI

Hi, I couldnt find any sample code neither in hibernate references nor community about how to make this @ManyToMany association “unidirectional”. Hibernate doesnt persist to the join table when you set a stock list to a category instance for this example.

And other question in my mind, why this manytomany solutions are considered poorly designed for databases. What other solutions can be done in situations like these.
Thanks. Ahmet BAYIRLI

Derek Lee
Guest
Derek Lee

For unidirectional remove stocks variable from Category.

Remove this, and all the associated methods.

private Set<Stock> stocks = new HashSet<Stock>(0);
Gagan
Guest
Gagan

I am getting the below error on executing the code. Exception in thread "main" org.hibernate.exception.SQLGrammarException: could not insert: [com.test.entities.CategoryEntity] at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:92) at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66) at org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:64) at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2345) at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2852) at org.hibernate.action.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:71) at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:273) at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:320) at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:203) at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:129) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:210) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:195) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:117) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93) at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:685) at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:677) at org.hibernate.engine.CascadingAction$5.cascade(CascadingAction.java:252) at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:392) at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:335) at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:204) at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:425) at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:362) at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:338) at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:204) at org.hibernate.engine.Cascade.cascade(Cascade.java:161) at org.hibernate.event.def.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:475) at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:353) at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:203) at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:129) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:210) at org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:56) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:195) at org.hibernate.event.def.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:50) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93) at org.hibernate.impl.SessionImpl.fireSave(SessionImpl.java:713) at org.hibernate.impl.SessionImpl.save(SessionImpl.java:701) at org.hibernate.impl.SessionImpl.save(SessionImpl.java:697) at com.test.App.main(App.java:74) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)… Read more »

trackback
Hibernate pro's in hur? - Fires of Heaven Guild Message Board

[…] this method, in JSP looping through a collection is very simple. I Copied my example from here: Hibernate – Many-to-Many example (Annotation) Let's say we have a collection of stocks which contain a collection of categories. […]

venkat
Guest
venkat

Thanks for Example, it is very detailed. I have one question,
If i delete a stock:
1) record from Stock table will be deleted
2) record from stock_category table will be deleted
3) record from category table will be deleted as well

But i don’t want to delete category from category table(step 3), how can we achieve that? Do we need to map something like in http://www.mkyong.com/hibernate/hibernate-many-to-many-example-join-table-extra-column-annotation/?

Miguel Mac
Guest
Miguel Mac

Hi,

In the case of you i to delete Stock with code = 7052 and all their association in the table stock_category (without delete de category record’s), what’s i must do?

Ex: categoryDAO.delete(stock);

Martin
Guest
Martin

Why not use @JoinTable ?

trackback
Hibernate – Many-to-many relationship example (XML Mapping)

[…] ?) Done Hibernate Annotation For many-to-many in Hibernate annotation, please refer to this example. Download it – Hibernate-many-to-many-xml-mapping.zip […]