Hibernate – Many-to-Many example – join table + extra column (Annotation)

In this tutorial, we show you how to use Hibernate to implements “many-to-many table relationship, with extra column in the join table“.

Note
For many to many relationship with NO extra column in the join table, please refer to this @many-to-many tutorial

1. Many-to-many table + extra columns in join table

The STOCK and CATEGORY many to many relationship is linked with a third / join table named STOCK_CATEGORY, with extra “created_by” and “created_date” columns.

many to many diagram

MySQL table script

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,
  `CREATED_DATE` DATE NOT NULL,
  `CREATED_BY` VARCHAR(10) 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. Project Structure

Review the file project structure of this tutorial.

many to many project folder

3. Hibernate / JPA Annotation

The Hibernate / JBoss tools generated annotation codes are not working in this third table extra column scenario. To make it works, you should customize the code to use “@AssociationOverride“, in StockCategory.java to represent the many to many relationship.

See following customized codes :

File : Stock.java

package com.mkyong.stock;
 
import java.util.HashSet;
import java.util.Set;
 
import javax.persistence.CascadeType;
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.OneToMany;
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<StockCategory> stockCategories = new HashSet<StockCategory>(0);
 
	public Stock() {
	}
 
	public Stock(String stockCode, String stockName) {
		this.stockCode = stockCode;
		this.stockName = stockName;
	}
 
	public Stock(String stockCode, String stockName,
			Set<StockCategory> stockCategories) {
		this.stockCode = stockCode;
		this.stockName = stockName;
		this.stockCategories = stockCategories;
	}
 
	@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;
	}
 
	@OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.stock", cascade=CascadeType.ALL)
	public Set<StockCategory> getStockCategories() {
		return this.stockCategories;
	}
 
	public void setStockCategories(Set<StockCategory> stockCategories) {
		this.stockCategories = stockCategories;
	}
 
}

File : StockCategory.java

package com.mkyong.stock;
 
import java.util.Date;
 
import javax.persistence.AssociationOverride;
import javax.persistence.AssociationOverrides;
import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
 
@Entity
@Table(name = "stock_category", catalog = "mkyongdb")
@AssociationOverrides({
		@AssociationOverride(name = "pk.stock", 
			joinColumns = @JoinColumn(name = "STOCK_ID")),
		@AssociationOverride(name = "pk.category", 
			joinColumns = @JoinColumn(name = "CATEGORY_ID")) })
public class StockCategory implements java.io.Serializable {
 
	private StockCategoryId pk = new StockCategoryId();
	private Date createdDate;
	private String createdBy;
 
	public StockCategory() {
	}
 
	@EmbeddedId
	public StockCategoryId getPk() {
		return pk;
	}
 
	public void setPk(StockCategoryId pk) {
		this.pk = pk;
	}
 
	@Transient
	public Stock getStock() {
		return getPk().getStock();
	}
 
	public void setStock(Stock stock) {
		getPk().setStock(stock);
	}
 
	@Transient
	public Category getCategory() {
		return getPk().getCategory();
	}
 
	public void setCategory(Category category) {
		getPk().setCategory(category);
	}
 
	@Temporal(TemporalType.DATE)
	@Column(name = "CREATED_DATE", nullable = false, length = 10)
	public Date getCreatedDate() {
		return this.createdDate;
	}
 
	public void setCreatedDate(Date createdDate) {
		this.createdDate = createdDate;
	}
 
	@Column(name = "CREATED_BY", nullable = false, length = 10)
	public String getCreatedBy() {
		return this.createdBy;
	}
 
	public void setCreatedBy(String createdBy) {
		this.createdBy = createdBy;
	}
 
	public boolean equals(Object o) {
		if (this == o)
			return true;
		if (o == null || getClass() != o.getClass())
			return false;
 
		StockCategory that = (StockCategory) o;
 
		if (getPk() != null ? !getPk().equals(that.getPk())
				: that.getPk() != null)
			return false;
 
		return true;
	}
 
	public int hashCode() {
		return (getPk() != null ? getPk().hashCode() : 0);
	}
}

File : StockCategoryId.java

package com.mkyong.stock;
 
import javax.persistence.Embeddable;
import javax.persistence.ManyToOne;
 
@Embeddable
public class StockCategoryId implements java.io.Serializable {
 
	private Stock stock;
    private Category category;
 
	@ManyToOne
	public Stock getStock() {
		return stock;
	}
 
	public void setStock(Stock stock) {
		this.stock = stock;
	}
 
	@ManyToOne
	public Category getCategory() {
		return category;
	}
 
	public void setCategory(Category category) {
		this.category = category;
	}
 
	public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
 
        StockCategoryId that = (StockCategoryId) o;
 
        if (stock != null ? !stock.equals(that.stock) : that.stock != null) return false;
        if (category != null ? !category.equals(that.category) : that.category != null)
            return false;
 
        return true;
    }
 
    public int hashCode() {
        int result;
        result = (stock != null ? stock.hashCode() : 0);
        result = 31 * result + (category != null ? category.hashCode() : 0);
        return result;
    }
 
}

File : Category.java

package com.mkyong.stock;
 
import java.util.HashSet;
import java.util.Set;
 
import javax.persistence.CascadeType;
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.OneToMany;
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<StockCategory> stockCategories = new HashSet<StockCategory>(0);
 
	public Category() {
	}
 
	public Category(String name, String desc) {
		this.name = name;
		this.desc = desc;
	}
 
	public Category(String name, String desc, Set<StockCategory> stockCategories) {
		this.name = name;
		this.desc = desc;
		this.stockCategories = stockCategories;
	}
 
	@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;
	}
 
	@OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.category")
	public Set<StockCategory> getStockCategories() {
		return this.stockCategories;
	}
 
	public void setStockCategories(Set<StockCategory> stockCategories) {
		this.stockCategories = stockCategories;
	}
 
}

Done, the many to many relationship should be working now.

4. Run it – Case 1

For a new category and a new stock.

    session.beginTransaction();
 
    Stock stock = new Stock();
    stock.setStockCode("7052");
    stock.setStockName("PADINI");
 
    Category category1 = new Category("CONSUMER", "CONSUMER COMPANY");
    //new category, need save to get the id first
    session.save(category1);
 
    StockCategory stockCategory = new StockCategory();
    stockCategory.setStock(stock);
    stockCategory.setCategory(category1);
    stockCategory.setCreatedDate(new Date()); //extra column
    stockCategory.setCreatedBy("system"); //extra column
 
    stock.getStockCategories().add(stockCategory);
 
    session.save(stock);
 
    session.getTransaction().commit();

Output…

Hibernate: 
    INSERT 
    INTO
        mkyongdb.category
        (`DESC`, NAME) 
    VALUES
        (?, ?)
Hibernate: 
    INSERT 
    INTO
        mkyongdb.stock
        (STOCK_CODE, STOCK_NAME) 
    VALUES
        (?, ?)
Hibernate: 
    SELECT
        stockcateg_.CATEGORY_ID,
        stockcateg_.STOCK_ID,
        stockcateg_.CREATED_BY AS CREATED1_2_,
        stockcateg_.CREATED_DATE AS CREATED2_2_ 
    FROM
        mkyongdb.stock_category stockcateg_ 
    WHERE
        stockcateg_.CATEGORY_ID=? 
        AND stockcateg_.STOCK_ID=?
Hibernate: 
    INSERT 
    INTO
        mkyongdb.stock_category
        (CREATED_BY, CREATED_DATE, CATEGORY_ID, STOCK_ID) 
    VALUES
        (?, ?, ?, ?)

5. Run it – Case 2

Get an existing category and a new stock.

   session.beginTransaction();
 
    Stock stock = new Stock();
    stock.setStockCode("7052");
    stock.setStockName("PADINI");
 
    //assume category id is 7
    Category category1 = (Category)session.get(Category.class, 7);
 
    StockCategory stockCategory = new StockCategory();
    stockCategory.setStock(stock);
    stockCategory.setCategory(category1);
    stockCategory.setCreatedDate(new Date()); //extra column
    stockCategory.setCreatedBy("system"); //extra column
 
    stock.getStockCategories().add(stockCategory);
 
    session.save(stock);
 
    session.getTransaction().commit();

Output…

Hibernate: 
    SELECT
        category0_.CATEGORY_ID AS CATEGORY1_1_0_,
        category0_.`DESC` AS DESC2_1_0_,
        category0_.NAME AS NAME1_0_ 
    FROM
        mkyongdb.category category0_ 
    WHERE
        category0_.CATEGORY_ID=?
Hibernate: 
    INSERT 
    INTO
        mkyongdb.stock
        (STOCK_CODE, STOCK_NAME) 
    VALUES
        (?, ?)
Hibernate: 
    SELECT
        stockcateg_.CATEGORY_ID,
        stockcateg_.STOCK_ID,
        stockcateg_.CREATED_BY AS CREATED1_2_,
        stockcateg_.CREATED_DATE AS CREATED2_2_ 
    FROM
        mkyongdb.stock_category stockcateg_ 
    WHERE
        stockcateg_.CATEGORY_ID=? 
        AND stockcateg_.STOCK_ID=?
Hibernate: 
    INSERT 
    INTO
        mkyongdb.stock_category
        (CREATED_BY, CREATED_DATE, CATEGORY_ID, STOCK_ID) 
    VALUES
        (?, ?, ?, ?)

Done.

Reference

  1. Hibernate Mapping Documentation
Tags :

About the Author

mkyong
Founder of Mkyong.com and HostingCompass.com, love Java and open source stuff. Follow him on Twitter, or befriend him on Facebook or Google Plus. If you like my tutorials, consider make a donation to these charities.

Comments

  • http://nowebsite JK

    Hi Mkyong,

    I am new to hibernate, I learned most of the hibernate from your site. I am trying to implement above example with following scenario:

    Trying to update an existing record in stock_category table with existing stock and category. but without any success. Can you post an example for above scenario.
    Example:

    stock
    —–
    stock_id= 1
    stock_id=2

    category
    ———
    category_id=1
    category_id=2

    stock_category
    —————
    stock_id =1, category_id=1

    How can I update stock_category existing record as follows

    stock_id=1 and category_id=2

    Can you help me to resolve the above scenario. an example would be great.

    Thanks,
    Jk

  • nikos

    Hi mKyong,

    i am trying to set up a similar example but i want to insert values to the join table.

    Could you please update your example by giving an example how to insert a value to your join table having the IDs of the other 2 tables??
    Thanks :)

  • Premier

    How i can delete the association?

  • Mazhar Hassan

    Excellent tutorial, can you please write a tutorail with same example but using XML mappings

    Thanks a lot

    Mazhar Hassan

  • NagendraBabu

    Same thing i was developed for sql but it’s creating an third table with raw datatype stock

  • Amit

    Hi Mk,
    While updating association of Stock_Category, older record were not deleted.
    They exist with new records in STCOK_CATEGORY table.

  • http://diegoneri.com Diego

    Thanks a lot for the example Mkyong!
    It been worked with also EntityManager!

    Cheers.

  • Martin Goik

    Your example allows to relate two Stock and Category instances. But how do you release (unlink) an existing relationship? It appears to me that the only way your code offers is by explicitely deleting the corresponding StockCategory object representing the link.

  • Jimmy

    @Manoj for xml mapping view this:

    http://www.skill-guru.com/blog/2010/02/10/mapping-composite-keys-in-hibernate/

    hope it will help!!

  • ashokayengar

    There is a simpler way to implement this.
    reference : http://forum.springsource.org/showthread.php?126461-How-to-realize-a-many-to-many-relation-type-with-attributes
    Although the example is using spring roo however it is perfectly applicable to regular JPA entity.

    Here is the example:
    Join entity needs the following annotation to ensure that there are not two entries with the same composite (not primary) key (human, qualification)

    @RooJavaBean
    @RooToString
    @RooJpaActiveRecord
    @Table(uniqueConstraints=@UniqueConstraint(columnN ames={&quot;human&quot;,&quot;qualification&quot;}), name=&quot;myUniqueConstraint&quot;)
    public class HumanQualification {
     
    @ManyToOne
    private Human human;
     
    @ManyToOne
    private Qualification qualification;
     
    private Float weight;
    }

    In Human and Qualification there must be “orphanRemoval=true” to delete corresponding join entities if a Human or Qualification is deleted:

    @OneToMany(cascade = CascadeType.ALL, mappedBy = &quot;human&quot;, orphanRemoval=true)
    private Set&lt;HumanQualification&gt; humanQualification = new HashSet&lt;HumanQualification&gt;();

    Hope this simplifies the solution

  • Thientb

    i run successfully but StockCategory not see. But…

    @Embeddable
    public class StockCategoryId implements java.io.Serializable {
     
    	private Stock stock;
        private Category category;
     
    	public Stock getStock() {
    		return stock;
    	}
     
    	public void setStock(Stock stock) {
    		this.stock = stock;
    	}
     
    	public Category getCategory() {
    		return category;
    	}
     
    	public void setCategory(Category category) {
    		this.category = category;
    	}

    when not “@ManyToOne” is have composite keys but haven’t relationship
    Please kindly help me. Thank you!!

  • manjeet rulhania

    nice explanation

  • Rafa

    Awesome, it works! Thank you!

  • chandan

    Hi I have used you example in my project:
    here goes the code:
    @Entity
    @Table(name=”RCPTDTL”)
    public class RcptDtl {

    @Column(name = “RCPTID”)
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long rcptId;
    @Column(name=”SOURCE_NAME”)
    private String sourceName;
    @Column(name=”DISP_NAME”)
    private String dispName;
    @Id
    @Column(name=”SOURCE_CSI”)
    private String sourceCsi;
    @OneToMany(fetch = FetchType.LAZY, mappedBy = “src_MapID.rcpt”)
    private Collection sourceMap = new ArrayList();

    }

    public class SourceMap implements java.io.Serializable {
    //EXtra coulm
    @Id
    @Column(name=”MAPID”)
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long mapId;
    @Column(name=”CREAT_DT”)
    private Date creatDt;
    @Column(name=”CREAT_BY”)
    private String creatBy;
    @Column(name=”UPD_DT”)
    private Date updDt;
    @Column(name=”UPD_BY”)
    private String updBy;
    @Column(name=”MAPVAL”)
    private String mapVal;
    //Extra coulm end
    @EmbeddedId
    private SourceMapID src_MapID = new SourceMapID();

    public SourceMapID getSrc_MapID() {
    return src_MapID;
    }

    public void setSrc_MapID(SourceMapID src_MapID) {
    this.src_MapID = src_MapID;
    }

    @Transient
    public PackageDtl getPackageDtl() {
    return getSrc_MapID().getPackageDtl();
    }
    public void setPackageDtl(PackageDtl packageDtl) {
    getSrc_MapID().setPackageDtl(packageDtl);
    }
    @Transient
    public RcptDtl getRcpt() {
    return getSrc_MapID().getRcpt();
    }
    }

    @Embeddable
    public class SourceMapID implements java.io.Serializable{

    private PackageDtl packageDtl;

    private RcptDtl rcpt;
    @ManyToOne
    public PackageDtl getPackageDtl() {
    return packageDtl;
    }
    public void setPackageDtl(PackageDtl packageDtl) {
    this.packageDtl = packageDtl;
    }
    @ManyToOne
    public RcptDtl getRcpt() {
    return rcpt;
    }
    public void setRcpt(RcptDtl rcpt) {
    this.rcpt = rcpt;
    }

    }

    @Entity
    @Table(name=”PACKAGEDTL”)
    public class PackageDtl {

    @Id
    @Column(name=”PID”)
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long pId;
    @Column(name=”ROSETTAID”)
    private long rosettaId;
    @Column(name=”DEFNAME”)
    private String defname;
    @Column(name=”MASTERID”)
    private String masterId;
    @Column(name=”CREAT_DT”)
    private Date creatDt;
    @Column(name=”CREAT_BY”)
    private String creatBy;
    @Column(name=”UPD_DT”)
    private Date updDt;
    @Column(name=”UPD_BY”)
    private String updBy;
    @OneToMany(fetch = FetchType.LAZY, mappedBy = “src_MapID.packageDtl”)
    private Collection sourceMap = new ArrayList();
    }

    But getting exception :
    Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘bookDao’ defined in class path resource [application.xml]: Cannot resolve reference to bean ‘HibernateTemplate’ while setting bean property ‘template'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘HibernateTemplate’ defined in class path resource [application.xml]: Cannot resolve reference to bean ‘sessionFactory’ while setting bean property ‘sessionFactory'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘sessionFactory’ defined in class path resource [application.xml]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: com.lexisnexis.rcpt.domain.SourceMap.src_MapID.rcpt in com.lexisnexis.rcpt.domain.RcptDtl.sourceMap

    Tried a lot but could not figure out what I am doing wrong ….

    • chandan

      Hi I am able to figure out the issue thanks a lot for your block it is wonder full.

      Do you have any example of binding java.util.collection member variable in spring form:form tag

      • chandan

        Hi I have one issue with you example in case of update all things are working good but the
        orphan object is not deleted after putting orphan=”true” also hibernate is not running the
        delete query.

        Any suggestion how the hibernate will delete the orphan object while updating will be really helpful …

        thanks
        Chandan

      • viiki

        Hey..How did you resolve this error?

  • chivu55

    I have a question. In the example 1 is the Stock the name “PADINI”. How I can change the name PADINI after session.getTransaction().commit();

  • Dansong

    I liked this article which is pretty helpful. However, if I only want to map the name column in category talbe -ie. instead of Colleciton, I want Collection which is a list of catory name only. Is there anyway I can do that? An XML format would be highly appreciated.

    Many thanks,

  • Adrien
  • Pingback: Spring 3 and Hibernate 4 for beginners « Solutions to basic IT problems()

  • Wisdom

    MKYOUNG, I am very thankful for your tutorials, Its helped me many times and thought to give you thanks at least. Keep up your working! Thanks again!

  • Pingback: hibernate many-to-many example | Jisku.com - Developers Network()

  • Pradeep

    Hi,

    Could you can provide the same example using xml mapping file?

    thanks,
    Pradeep

  • paul

    Hello,

    I have following problem/question…I have been told that the orm could or increase scallability of the whole application. By increasing scallability i mean situation in which I add new data sets into tables or increase the complexity (perform sufisticated for example select based on i.e. join)and see that the time of the response of my application increases much less than in situation when I’d use jdbc. Please dont confuse that im trying to prove that jdbc is slover than orm…Can u put some comment to this. Im interested how in the best way show this in my app.

    Thx in advance.

    • http://www.mkyong.com mkyong

      You should Google for more, just highlight brief compare between JDBC vs ORM :

      JDBC is simple, fast, and easy to learn. Good for simple application, bad for scalability and large system, because you hardcode the SQL, and the hardcode is just hard to maintain. And, you need to know SQL to write JDBC.

      ORM is easy to maintain, coz no more hardcode, you don’t need to know SQL to write code, and everything is Object. But, it is slightly slower than JDBC, and take time to learn if the table < -> data model is complex. When code in complex table relationship or large join, ORM is bad.

  • Anish Abraham

    Nice article, I have an issue with multiple foreign keys, your language is understandable, could you please check it https://forum.hibernate.org/viewtopic.php?f=1&t=1016943

  • toni

    Hello. Thanks for the great tutorial. What i was search all over was a nice example on how to update a many to many associated relationship. For example you have your stock with some category. Now you you remove some categories and add different ones. I didn’t manage to do it properly with hibernate.. :-( I got it to work that all categories where removed or where i suddenly had always doubled records. To be more precise, i didn’t used exactly your example but also i took it as reference for my code. It would be nice to see a code example where you see added/removed stuff. Anyway, thanks a lot again!! Cheers

  • An user

    Where is the tutorial? I see only a bunch of unreadable code. What do those lines of code mean?

  • antony

    this tuto is to great, i transform it with attribute override to make one to many association with extra columns

  • Mehdi.M

    hi
    in your case you have check you many to one mappin
    when you choose class=” you r class” your class in not set to object yo define in getter setter
    have enjoy ;)

  • http://www.buscafreela.com.br Marc Schipperheyn

    I suggest logging a bug with Hibernate on this

  • Dexy

    Hi mkyong,

    This example works fine, but there’s a performance problem with Hibernate when you run a HQL like this:

    SELECT s
    FROM Stock s
    LEFT JOIN FETCH s.stockCategories cRel
    WHERE s.stockId = :id

    i.e. you want to fetch a stock with all its all categories in one SQL (avoiding n+1 selects)

    This fetch doesn’t work properly. Actually, it does the join and executes the correct SQL, but immediately after that, for each stock_category Hibernate executes additional SQLs to fetch the associated stock and category separately!!!

    I think it’s a bug in Hibernate that forces separate SQLs for each part of a composite PK if it’s entity type.

    Do you maybe know a workaround for this?

    Tnx,
    Dexy

  • p00m

    Hey Mkyong

    Great examples & tutorials!

    But I’m having some trouble adding another table to the jointable. Using your example what I need is to add lets say a table projects connected to the stock_category table.

    In my case stock is users and category is projects and the added table would be the number of hours worked on a specific project (using a date).

    A database example:
    http://i48.tinypic.com/4q1k48.png

    Any idea how I could do this?

    Cheers

    p00m

  • John Hunsley

    Thanks for the great tutorial.

    One issue i’ve found when using this strategy is you might get a NullPointer when hibernate tries to set the id values in the EmbeddedId object. This comes out in the stack trace with the following exception –

    org.hibernate.PropertyAccessException: could not set a field value by reflection setter …..

    I solved it by constructing a blank EmbeddedId instance in the constructor of the class in which the id is embedded. In your example i’d add the following –

    public StockCategory() {
    pk = new StockCategoryId;
    }

  • aminos

    Hi , thanks for this tutorial I want the same solution but with xml , if u can help me thanks I searched at google and stackoverflow but i didnt found a solution

  • Rakesh

    New at Hibernate .. pardon my ignorance

    In the example that you have shown .. Is there a way to get set of Category for given stock entity or vice verse return set of Stock for given Category?

    Thanks
    Rakesh

  • Joe

    Mkyong, I posted a question to StackOverflow regarding this tutorial with my custom requirements. Would you mind taking a look when you have a chance? http://stackoverflow.com/questions/9549294/looping-through-hibernate-many-to-many-in-jsp I’ve been battling with this for about a week. Thanks for your helpful blog!

  • http://iems.df.gob.mx/ Abel Bravo Camacho

    Using criteria for quering…

    DetachedCriteria criteria = DetachedCriteria.forClass(StockCategory.class,”stock-cat”);
    criteria.createAlias(“stock-cat.pk”,”p”);
    criteria.createAlias(“p.stock”,”st”);
    criteria.add(Restrictions.eq(“st.stockName”, “abc”);

    When this query run it throws:
    org.postgresql.util.PSQLException: ERROR: missing FROM-clause entry for table “st1_”

    what is wrong?

    thanks in advance!

  • http://iems.df.gob.mx/ Abel Bravo Camacho

    Well I got the assocition, but how query a extra field from de join class using Criteria?
    Thanks for your help

  • http://iems.df.gob.mx/ Abel Bravo Camacho

    How do you persist a stockCategory instance getting an existing stock and an existing category?

    Thanks in advance!

  • Kedar

    Can you give me some pointers on how to delete the mapping. I tried removing the StockCategory from the attached set and updating the Stock product. Though it updated without errors the records were not deleted from join table. My implementation is in spring.

    Essentially my code is

    Stock stock = loadStock(stockId)
     
    StockCategory associationToRemove = new StockCategory();
    associationToRemove.setStock(stock);
    associationToRemove.setCategory(loadCategory(catId));
    stock.getStockCategory().remove(associationToRemove);
     
    update(stock);

    Fetch and assign works as expected.

    Thanks!

    • Kedar

      Got it. Had to delete my association explicitly

  • Chris457

    Dear Mr. MkYong,
    I frequently enjoy your blog and it has given me a number of solutions for problems. Thank you for using your time to share your knowledge!
    Having said that I was wondering whether this article’s initial statement:
    “The Hibernate / JBoss tools generated annotation codes are not working in this third table extra column scenario.”
    was really true.
    To clarify, I used the “JPA-Project” under Eclipse Java EE IDE for Web Developers “Indigo”, apparently a different tool(!?) but it ships with my Eclipse-version, so no need for an extrag plugin.
    My Hibernate Version is hibernate-distribution-3.5.0-Final with Java 1.6.
    I used your CREATE TABLE-SQL, adapting it for HSQLDB, and had Eclipse generate the Java classes.
    The most striking difference to your solution is the generated

    @Embeddable
    public class StockCategoryPK implements Serializable {
    	private int stockId;
    	private int categoryId;
            ...

    which, as one can see, only contains the keys of the Stock and Category-objects as opposed to the entire object-references in your solution.
    The latter are, seemingly superfluously, added to the StockCategory class as well by the code generation:

    @Entity
    @Table(name=&quot;STOCK_CATEGORY&quot;)
    public class StockCategory implements Serializable {
    	private static final long serialVersionUID = 1L;
    	private StockCategoryPK id;
    	private String createdBy;
    	private Date createdDate;
    	private Category category;//seems superfluous, but...
    	private Stock stock;//seems superfluous, but...

    However, I found that in order to make it work one has to assign ONLY the

            private StockCategoryPK id;

    -field
    and NOT the

    	private Category category;
    	private Stock stock;

    after a new Category- and Stock-Object respectively have been persisted first.
    A simple JPQL “select distinct sc from StockCategory sc” will reveal that the stock- and category-fields in all the StockCategory-objects are indeed assigned by JPA/Hibernate!
    So, to clarify, the entire persisting-sequence would be:

    Stock stock = new Stock();
    stock.setStockCode(&quot;7052&quot;);
    stock.setStockName(&quot;PADINI&quot;);
    manager.persist(stock);
    Category category1 = new Category(&quot;CONSUMER&quot;, &quot;CONSUMER COMPANY&quot;);
    manager.persist(category1);
    StockCategory stockCategory = new StockCategory();
    stockCategory.setCreatedDate(new Date());
    stockCategory.setCreatedBy(&quot;system&quot;);
    StockCategoryPK scpk = new StockCategoryPK();
    scpk.setCategoryId(category1.getCategoryId());
    scpk.setStockId(stock.getStockId());
    stockCategory.setId(scpk);
    stock.getStockCategories().add(stockCategory);
    //optional:			        category1.getStockCategories().add(stockCategory);
    manager.persist(stockCategory);  
    System.out.println(&quot;Done&quot;);

    So that’s 16 lines of persist-code as opposed to your 13 lines.

    To me, that’s a tradeoff which should be carefully considered:
    a) The scenario is common in relational databases (doctor-treats-patient at a specific time and with a specific diagnosis, sportsteam-meets-sportsteam at a specific time with a endresult, etc…) and I would really like to use generated code for that.
    b)Your solution makes a number of changes necessary which are spread out over all the generated classes. Forgetting one is fatal, for instance:
    c) With your solution the cascade=CascadeType.ALL in

    public class Stock implements java.io.Serializable {
    ...
    @OneToMany(fetch = FetchType.LAZY, mappedBy = &quot;pk.stock&quot;, cascade=CascadeType.ALL)
    	public Set&lt;StockCategory&gt; getStockCategories() {
    		return this.stockCategories;
    	}

    is mandatory for this to work (otherwise table STOCK_CATEGORY would be empty)
    In the generated code it isn’t!
    d) Accessing a Stock’s Category-objects via it StockCategory-Set is slightly easier in JPQL. With the generated code the query woud be:

    &quot;select distinct c.category from Stock s join s.stockCategories c&quot;

    With your solution it’s:

    &quot;select distinct c.pk.category from Stock s join s.stockCategories c&quot;

    e) Finally, the least important, but still: The Eclipse JPA-Project doesn’t recognize your solution and keeps flagging an error at design time. This error is only caused by the JPA-facette of Eclipse and doesn’t prevent the code from compiling and running, but it is annoying…
    I will gladly send you my entire Eclipse-project for your inspection, should you be interested and, since this is my first response to one of your articles, would be delighted if you commented it!
    Thank you for your time
    yours sincerely
    Chris457

    • http://www.mkyong.com mkyong

      Hi Chris, Thanks for your long comment and i am really appreciated it, but my reply will be short :)

      Current JBoss tool generated codes are not able to fulfill my needs for third table which has extra columns, so i have to hack it. The way it handle the relationship is weird and may prompts warning in IDE, but it’s works at my end (so, who’s care?). In additional, i just can’t find any “standard” solution on Hibernate documentation.

      hehe… may be you should send a request to Hibernate team to ask them put a standard solution on Hibernate documentation.

  • http://www.buscafreela.com.br Marc Schipperheyn

    Hi,

    I’d like to get some feedback on this approach vs ManyToMany in relation to lazy loading and using Set. When you insert an item into a Set, in a @ManyToMany, usually the whole set will be loaded to ensure uniqueness. However, since the stockCategory represents the two ids of the related items, an equals comparison could be done solely based on the ids and the related entity should not have to load. Is this true? If so, would this work like this in the above example?

  • http://www.dafoot.co.uk DaFoot

    Hello,
    Thankyou for taking the time to create/share the tutorial.

    I’ve tried to implement it in my Spring/Hibernate application but I seem to get some sort of object nesting recursion going when I make the call for fetch an object from one side of the relationship.

    I’ve detailed the problem/code on coderanch here:
    http://www.coderanch.com/t/560053/Spring/Spring-hibernate-manytomany-mapping-problems

    And on Spring Source here:
    http://forum.springsource.org/showthread.php?118938-Hibernate-manytomany-mappings

    I suspect it’s something silly/obvious but as a noob to Spring/Hibernate I’m struggling. If you could spare 10 minutes to have a quick look for anything obvious it would be much appreciated :)

    • Ilan

      I have the same problem. Did you solve the problem?

  • Nassa

    Hello,
    I used your ManyToMany example with additionnals columns in the join table with hibernate 3.6.1.
    – When I save the Stock entity with categories associated : All is Ok.
    – When I read the Stock entity : All is Ok.
    – When I delete The Stocke entity the joint table records are also removed : All is Ok
    – But when I update the Stock entity (without to change associated categories) Hibernate go to stack overflow error :

    java.lang.StackOverflowError
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:431)
    at com.mysql.jdbc.PreparedStatement.getInstance(PreparedStatement.java:872)
    at com.mysql.jdbc.ConnectionImpl.clientPrepareStatement(ConnectionImpl.java:1490)
    at com.mysql.jdbc.ConnectionImpl.prepareStatement(ConnectionImpl.java:4253)
    at com.mysql.jdbc.ConnectionImpl.prepareStatement(ConnectionImpl.java:4152)
    at org.hibernate.jdbc.AbstractBatcher.getPreparedStatement(AbstractBatcher.java:534)
    at org.hibernate.jdbc.AbstractBatcher.getPreparedStatement(AbstractBatcher.java:452)
    at org.hibernate.jdbc.AbstractBatcher.prepareQueryStatement(AbstractBatcher.java:161)
    at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1700)
    at org.hibernate.loader.Loader.doQuery(Loader.java:801)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:274)
    at org.hibernate.loader.Loader.loadEntity(Loader.java:2037)
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:86)
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:76)
    at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3293)
    at org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:496)
    at org.hibernate.event.def.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:477)
    at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:227)
    at org.hibernate.event.def.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:285)
    at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:152)
    at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:1090)
    at org.hibernate.impl.SessionImpl.internalLoad(SessionImpl.java:1038)
    at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:630)
    at org.hibernate.type.EntityType.resolve(EntityType.java:438)
    at org.hibernate.type.ComponentType.resolve(ComponentType.java:617)
    at org.hibernate.loader.Loader.extractKeysFromResultSet(Loader.java:722)
    at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:606)
    at org.hibernate.loader.Loader.doQuery(Loader.java:829)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:274)
    at org.hibernate.loader.Loader.loadEntity(Loader.java:2037)
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:86)
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:76)
    at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3293)
    at org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:496)
    at org.hibernate.event.def.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:477)
    at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:227)
    at org.hibernate.event.def.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:285)
    at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:152)
    at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:1090)
    at org.hibernate.impl.SessionImpl.internalLoad(SessionImpl.java:1038)
    at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:630)
    at org.hibernate.type.EntityType.resolve(EntityType.java:438)
    at org.hibernate.type.ComponentType.resolve(ComponentType.java:617)
    at org.hibernate.loader.Loader.extractKeysFromResultSet(Loader.java:722)
    at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:606)
    at org.hibernate.loader.Loader.doQuery(Loader.java:829)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:274)
    at org.hibernate.loader.Loader.loadEntity(Loader.java:2037)

    • beginner_

      I have exactly the same issue in hibernate4. Were you able to resolve this?

  • JohnJ

    Hello,

    I was looking at Case1 and I don’t see where category1.stockCategories is being updated.
    Maybe I am missing something ?

    Thanks

    John

  • G Suresh

    Useful example for many to many

  • Manoj

    I followed the xml mapping as in the attached zip
    but when trying to persist object I got the following error

    In java when trying to save the object

    Stocks stock = new Stocks();
    		stock.setStockCode("7052");
    	    stock.setStockName("PADINI");
    	   // session().save(stock);
     
    	    Cat cat1 = new Cat();
    	    cat1.setName("consumer");
    	    cat1.setDesc("consumer company");
    	    //new category, need save to get the id first
     
    	    session().save(cat1);
     
     
     
    	    StockCategory stockCategory = new StockCategory();
    	    stockCategory.setCategoryId(cat1);
    	    stockCategory.setStockId(stock);
    	    stockCategory.setCreatedDate(new Date());
    	    stockCategory.setCreatedBy("user");
     
    	    stock.setStockCategories(new HashSet());
     
    	    StockCategoryId scId = new StockCategoryId();
    	    stock.getStockCategories().add(stockCategory);
     
    	    session().save(stock);

    Following hibernate error is generated

    org.springframework.orm.hibernate3.HibernateSystemException: 
       ids for this class must be manually assigned before calling save(): com.mkyong.ternary.StockCategory; 
       nested exception is org.hibernate.id.IdentifierGenerationException: 
       ids for this class must be manually assigned before calling save(): com.mkyong.ternary.StockCategory
    	org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:661)
    	org.springframework.orm.hibernate3.HibernateAccessor.convertHibernateAccessException(HibernateAccessor.java:412)

    Can you please help me to solve this issue

    • http://www.mkyong.com mkyong

      The ids didn’t assign properly. Seeing your error description is rather hard to tell you what is the root caused. My advice is download this project, and run it without any modification, and then compare with yours.

      • Manoj

        Dear MK Yong
        Thanks for the reply. I desperately looking for the solution of this problem.

        As you suggested, I download the code as it is and run.

        I have used the xml mapping, so I removed the annotation and I end up with following code. Actually I need a solution in xml-mapping so I removed the annoation.

        but still get the error. Pls see below

        public class Category implements java.io.Serializable {
        	private Integer categoryId;
        	private String name;
        	private String desc;
        	private Set stockCategories = new HashSet(0);
         
        	public Category() {
        	}
         
        	public Category(String name, String desc) {
        		this.name = name;
        		this.desc = desc;
        	}
         
        	public Category(String name, String desc, Set stockCategories) {
        		this.name = name;
        		this.desc = desc;
        		this.stockCategories = stockCategories;
        	}
         
        	public Integer getCategoryId() {
        		return categoryId;
        	}
         
        	public void setCategoryId(Integer categoryId) {
        		this.categoryId = categoryId;
        	}
         
        	public String getName() {
        		return name;
        	}
         
        	public void setName(String name) {
        		this.name = name;
        	}
         
        	public String getDesc() {
        		return desc;
        	}
         
        	public void setDesc(String desc) {
        		this.desc = desc;
        	}
         
        	public Set getStockCategories() {
        		return stockCategories;
        	}
         
        	public void setStockCategories(Set stockCategories) {
        		this.stockCategories = stockCategories;
        	}
         
         
         
        }
         
        public class Stock implements java.io.Serializable {
        	private Integer stockId;
        	private String stockCode;
        	private String stockName;
        	private Set stockCategories = new HashSet(0);
         
         
        	public Integer getStockId() {
        		return stockId;
        	}
        	public void setStockId(Integer stockId) {
        		this.stockId = stockId;
        	}
        	public String getStockCode() {
        		return stockCode;
        	}
        	public void setStockCode(String stockCode) {
        		this.stockCode = stockCode;
        	}
        	public String getStockName() {
        		return stockName;
        	}
        	public void setStockName(String stockName) {
        		this.stockName = stockName;
        	}
        	public Set getStockCategories() {
        		return stockCategories;
        	}
        	public void setStockCategories(Set stockCategories) {
        		this.stockCategories = stockCategories;
        	}
         
         
        }
        public class StockCategory implements java.io.Serializable {
        	private StockCategoryId pk = new StockCategoryId();
        	private Date createdDate;
        	private String createdBy;
         
        	public StockCategory() {
        	}
         
        	public Stock getStock(){
        		return getPk().getStock();
        	}
         
        	public void setStock(Stock stock) {
        		getPk().setStock(stock);
        	}
         
        	public Category getCategory() {
        		return getPk().getCategory();
        	}
         
        	public void setCategory(Category category) {
        		getPk().setCategory(category);
        	}
        	public boolean equals(Object o) {
        		if (this == o)
        			return true;
        		if (o == null || getClass() != o.getClass())
        			return false;
         
        		StockCategory that = (StockCategory) o;
         
        		if (getPk() != null ? !getPk().equals(that.getPk())
        				: that.getPk() != null)
        			return false;
         
        		return true;
        	}
         
        	public int hashCode() {
        		return (getPk() != null ? getPk().hashCode() : 0);
        	}
        	public StockCategoryId getPk() {
        		return pk;
        	}
         
        	public void setPk(StockCategoryId pk) {
        		this.pk = pk;
        	}
         
        	public Date getCreatedDate() {
        		return createdDate;
        	}
         
        	public void setCreatedDate(Date createdDate) {
        		this.createdDate = createdDate;
        	}
         
        	public String getCreatedBy() {
        		return createdBy;
        	}
         
        	public void setCreatedBy(String createdBy) {
        		this.createdBy = createdBy;
        	}
         
         
        }
        public class StockCategoryId implements java.io.Serializable {
         
        	private Stock stock;
            private Category category;
         
         
        	public Stock getStock() {
        		return stock;
        	}
        	public void setStock(Stock stock) {
        		this.stock = stock;
        	}
        	public Category getCategory() {
        		return category;
        	}
        	public void setCategory(Category category) {
        		this.category = category;
        	}
        	public boolean equals(Object o) {
                if (this == o) return true;
                if (o == null || getClass() != o.getClass()) return false;
         
                StockCategoryId that = (StockCategoryId) o;
         
                if (stock != null ? !stock.equals(that.stock) : that.stock != null) return false;
                if (category != null ? !category.equals(that.category) : that.category != null)
                    return false;
         
                return true;
            }
         
            public int hashCode() {
                int result;
                result = (stock != null ? stock.hashCode() : 0);
                result = 31 * result + (category != null ? category.hashCode() : 0);
                return result;
            }
         
        }

        When I run the code

        Stock stock = new Stock();
        	        stock.setStockCode("7052");
        	        stock.setStockName("PADINI");
         
         
        	        Category category1 = new Category("CONSUMER", "CONSUMER COMPANY");
        	        //new category, need save to get the id first
        	        session.save(category1);
         
         
        	        StockCategory stockCategory = new StockCategory();
         
        	        stockCategory.setStock(stock);
        	        stockCategory.setCategory(category1);
        	        stockCategory.setCreatedDate(new Date());
        	        stockCategory.setCreatedBy("system");
         
        	        stock.getStockCategories().add(stockCategory);
        	        session.save(stock);

        I got the following error

        Hibernate: insert into category (NAME, `DESC`) values (?, ?)
        Hibernate: insert into stock (STOCK_CODE, STOCK_NAME) values (?, ?)
        Hibernate: select stockcateg_.STOCK_ID, stockcateg_.CATEGORY_ID, stockcateg_.CREATED_DATE as CREATED3_5_, 
        stockcateg_.CREATED_BY as CREATED4_5_ from stock_category stockcateg_ where stockcateg_.STOCK_ID=? and stockcateg_.CATEGORY_ID=?
        1427 [http-8090-2] ERROR org.hibernate.property.BasicPropertyAccessor - IllegalArgumentException in class: com.mkyong.stock.Stock, getter method of property: stockId
        org.hibernate.PropertyAccessException: IllegalArgumentException occurred calling getter of com.mkyong.stock.Stock.stockId
        	at org.hibernate.property.BasicPropertyAccessor$BasicGetter.get(BasicPropertyAccessor.java:195)
        	at org.hibernate.tuple.entity.AbstractEntityTuplizer.getIdentifier(AbstractEntityTuplizer.java:199)
        	at org.hibernate.persister.entity.AbstractEntityPersister.getIdentifier(AbstractEntityPersister.java:3605)
        	at org.hibernate.persister.entity.AbstractEntityPersister.isTransient(AbstractEntityPersister.java:3321)
        • http://www.mkyong.com mkyong

          I didn’t test it with XML mapping, caused quite complicated to implement this in XML way, is the annotation version working?

          • Manoj

            yes, that was fine. But I need to work with xml mapping and i could not found anywhere on google :(

          • http://www.mkyong.com mkyong

            This kind of model is quite complicated to configure in XML mapping….hmm…

            Try post your question on https://forum.hibernate.org/ or http://stackoverflow.com/ , update me if you have answer :)

          • Chethan S

            The Xml Mapping worked only if we do individual save like stock,category followed by StockCategory.

             
            session.beginTransaction();
             
            		Stock stock = new Stock();
            	        stock.setStockCode(&quot;7052&quot;);
            	        stock.setStockName(&quot;PADINI&quot;);
            	        stock.setStockId(10);
            	     session.save(stock);
            	        Category category1 = new Category(11,&quot;CONSUMER&quot;, &quot;CONSUMER COMPANY&quot;);
            	      // Category category2 = new Category(12,&quot;INVESTMENT&quot;, &quot;INVESTMENT COMPANY&quot;);
             
            	       session.save(category1);
             
             
             
            	        StockCategory stockcat=new StockCategory();
            	      stockcat.setStock(stock);
            	       stockcat.setCategory(category1);
            	        stockcat.setCreatedBy(&quot;me&quot;);
            	        stockcat.setCreatedDate(new Date());
             
             
            	        StockCategoryId stoc_Cat_id=new StockCategoryId();
            	        stoc_Cat_id.setCategoryId(category1.getCategoryId());
            	        stoc_Cat_id.setStockId(stock.getStockId());
             
            	        stockcat.setId(stoc_Cat_id);
             
            	 //  stock.getStockCategories().add(stockcat);
            	     //category1.getStockCategories().add(stockcat); 
             
            	     // session.save(stock);
             
             
            	 session.save(stockcat);
             
            		session.getTransaction().commit();
  • Manoj

    Can you please tell me which tutorial?
    I need xml mapping for many-to-many with extra columns.

  • DE OLIVEIRA

    Hello,

    Thanks for this example. I have errors on
    @AssociationOverrides({
    @AssociationOverride(name = “pk.stock”,
    joinColumns = @JoinColumn(name = “STOCK_ID”)),
    @AssociationOverride(name = “pk.category”,
    joinColumns = @JoinColumn(name = “CATEGORY_ID”)) })

    Errors are :
    – Persistent type of override attribute pk.stock cannot be resolved
    – Persistent type of override attribute pk.category cannot be resolved

    Any idea about this problem ?
    Thanks for your help

    • http://www.mkyong.com mkyong

      Is this compile error? or runtime error?

      Exampled tested in Hibernate 3.6, Eclipse 3.6, Maven3 and JDK1.6

      • DE OLIVEIRA

        It is a compile error.

        Eclipse 3.7, JDK 1.6, Hibernate-JPA 2.0

        • http://www.mkyong.com mkyong

          At least post your compile error message for debug :)

          • DE OLIVEIRA

            Errors are listed on my first post :)

    • antony

      hi, i had the same problem as you but it was because i used jpa. don’t use jpa to do it. use pure hibernate and the file hibernate.cfg.xml

  • http://n/a Jemaru

    Hi Sir mKyong,

    Can you please help me? I want to join 3 tables in 1 join table. Ex. Table User, Application, and Role will be joined in a table User_App_Role containing IDs of each tables.

    I tried to follow your example and here is my codes:

    User.java

    	@OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.cmUser")
    	public Collection getCMUserApplicationRole() {
    		return this.cmUserAppRole;
    	}
     
    	public void setCMUserApplicationRole(Collection cmUserAppRole) {
    		this.cmUserAppRole = cmUserAppRole;
    	}
     
    	private Collection cmUserAppRole = new ArrayList(0);

    CMApplication.java

    	@OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.cmApplication")
    	public Collection getCMUserApplicationRole() {
    		return this.cmUserAppRole;
    	}
     
    	public void setCMUserApplicationRole(Collection cmUserAppRole) {
    		this.cmUserAppRole = cmUserAppRole;
    	}
     
    	private Collection cmUserAppRole = new ArrayList(0);

    CMRole.java

    	@OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.cmRole")
    	public Collection getCMUserApplicationRole() {
    		return this.cmUserAppRole;
    	}
     
    	public void setCMUserApplicationRole(Collection cmUserAppRole) {
    		this.cmUserAppRole = cmUserAppRole;
    	}
     
    	private Collection cmUserAppRole = new ArrayList(0);

    CMUserApplicationRole.java

    @Entity
    @Table(name = "CMUserApplicationRole")
    @AssociationOverrides({
    		@AssociationOverride(name = "pk.cmUser", joinColumns = @JoinColumn(name = "USER_ID")),
    		@AssociationOverride(name = "pk.cmApplication", joinColumns = @JoinColumn(name = "APPLICATION_ID")),
    		@AssociationOverride(name = "pk.cmRole", joinColumns = @JoinColumn(name = "ROLE_ID"))})

    	@EmbeddedId
    	public CMUserApplicationRoleId getPk() {
    		return pk;
    	}
     
    	public void setPk(CMUserApplicationRoleId pk) {
    		this.pk = pk;
    	}
     
    	@Transient
    	public CMUser getCMUser() {
    		return getPk().getCMUser();
    	}
     
    	public void setCMUser(CMUser cmUser) {
    		getPk().setCMUser(cmUser);
    	}
     
    	@Transient
    	public CMApplication getCMApplication() {
    		return getPk().getCMApplication();
    	}
     
    	public void setCMApplication(CMApplication cmApplication) {
    		getPk().setCMApplication(cmApplication);
    	}
     
    	@Transient
    	public CMRole getCMRole() {
    		return getPk().getCMRole();
    	}
     
    	public void setCMRole(CMRole cmRole) {
    		getPk().setCMRole(cmRole);
    	}

    CMUserApplicationRoleId.java

    	@ManyToOne
    	public CMUser getCMUser() {
    		return cmUser;
    	}
    	public void setCMUser(CMUser cmUser) {
    		this.cmUser = cmUser;
    	}
     
    	@ManyToOne
    	public CMApplication getCMApplication() {
    		return cmApplication;
    	}
    	public void setCMApplication(CMApplication cmApplication) {
    		this.cmApplication = cmApplication;
    	}
     
    	@ManyToOne
    	public CMRole getCMRole() {
    		return cmRole;
    	}
    	public void setCMRole(CMRole cmRole) {
    		this.cmRole = cmRole;
    	}
     
    	private CMUser cmUser;
    	private CMApplication cmApplication;
    	private CMRole cmRole;

    When I tried to run the code:

    session.beginTransaction();
    session.save(user);
    session.save(app1);
    session.save(role1);
     
    CMUserApplicationRole cmUserAppRole = new CMUserApplicationRole();
    cmUserAppRole.setCMApplication(app1);
    cmUserAppRole.setCMRole(role1);
    cmUserAppRole.setCMUser(user);
     
    session.save(cmUserAppRole);
    session.getTransaction().commit();

    I got the ff error:

    org.hibernate.MappingException: Could not determine type for: java.util.Collection, at table: CM_ROLE, for columns: [org.hibernate.mapping.Column(cmUserAppRole)]

    Please kindly help me. Thank you!

    • Derek Lee

      This error typically occurs when you are mixing “field” and “property” access strategy.
      From what you have pasted up there I cannot tell if you put the

      @Id

      annotation above the method or attribute, so make sure you only stick to one access strategy. :)

  • Martin

    Thanks, but you should not talk about Hibernate. THIS is JPA implemented using Hibernate. All of the code in this article would work using any other Java Persistence Api provider.

    • http://www.mkyong.com mkyong

      Should be JPA annotation in Hibernate project

      • Basawaraj

        What id stock_category table has one more column stockcategoryid as primary key??

  • Pingback: Hibernate – Many-to-Many example (XML Mapping)()