In this tutorial, we show you how to use Hibernate to implements “many-to-many table relationship, with extra column in the join table“.
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.
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.
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.
Hello Sir,
When i am trying to execute <stock.getStockCategories()> statement then i am getting following error:
ClassName=o.h.e.loading.internal.LoadContexts Message=HHH000100: Fail-safe cleanup (collections) : org.hibernate.engine.loading.internal.CollectionLoadContext@6c1548c0<rs=HikariProxyResultSet@1324915780 wrapping oracle.jdbc.driver.ForwardOnlyResultSet@7ab2d65e>
can you please help me here please
Getting following error
Caused by: org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: NULL not allowed for column “STOCK_ID”; SQL statement:
Hi, thanks for your example. I have a problem I want to add unique constraint on table stockCategory on two fields ” pk.stock and pk.category” when I create this IN StockCategory table I give this error:
Unable to create unique key constraint (STOCK_ID, CATEGORY_ID) on table StockCategory : database column ‘STOCK_ID’, ‘CATEGORY_ID’ not found. Make sure that you use the correct column name which depends on the naming strategy in use (it may not be the same as the property name in the entity, especially for relational type
You should have explained the code at least!
Hi,
Nice explanation, good article.
However, when I am saving the parent entity it is throwing
org.springframework.dao.DataIntegrityViolationException: A different object with the same identifier value was already associated with the session.
My use case is pretty much same,
Category table already has records and trying to update the references from Stock and Category Set with references to create mapping.
Problems faced:-
1. That above exception.
2. orphanRemoval=true is also not working.
3. update and delete to the stock_category doesn’t work
Anybody who faced the same problem as mine? Please help me out.
Hi I am getting below compile time error.
Persistent type of override attribute “pk.stock” cannot be resolved
Persistent type of override attribute “pk.catagory” cannot be resolved
I am using JPA,jdk7,eclipse mars2…. Please help me out.
Coool Article! Thanks for sharing your knowledge 🙂
isn’t the job of jpa/hibernate is to make us focus on object oriented programming rather than thinking in solving relational model problem? because this solution is relational model oriented.
mkyong, thank you for this tutorial. Can you tell me if there is a way to use hibernate ‘criteria’ to query with conditions on the ‘middle’ table. So for example I want to get the stock for a particular category that has been created by a certain person?
Awesome tutorial! Unfortunately, I’m having trouble updating and/or deleting entries. Can someone please provide some examples of how to delete and update?
how to run if both table have data (stock and stok category)
How can we get list of categories of a stock or stocks of a category?
Everything works until i add @Audited annotation. Seems that Envers can’t process “mapped by” attribute.
Caused by: org.hibernate.MappingException: Unable to read the mapped by attribute for stockCategories in com.mkyong.stock.StockCategory!
I’m using Hibernate and Envers 4.3.7.Final.
Have you found the solution for this issue?
What is the reason why you did not use two many-to-one’s in the junction class? So you neither have to touch Student nor Class
The solution looks overly complicated.
If I try to convert the objects to json using Jackson, I either get org.codehaus.jackson.map.JsonMappingException: failed to lazily initialize a collection of role: com.mkyong.stock.Stock.stockCategories or if I fetch EAGER, I get infinite recursion exception. Do you know a solution to this? I tried @JsonIgnore, @JsonManagedReference, and @JsonIdentityInfo but none work.
In this situations, you need to avoid using bidirectional relation – that is what is creating infinite recursion.
For example : Instead of
@OneToMany(mappedBy=”property_in_other_Entity”)
private Set columns;
…you could use
@javax.persistence.Transient
private Set columns;
Good tutorial.
I have a prblem when I try to save more records . I get the following error:
org.hibernate.NonUniqueObjectException: A different object with the same identifier value was already associated with the session :
This is my code:
public void addContacto(Proyecto proyecto, Contacto contacto, String tipo) {
Session session = sessionFactory.openSession();
Transaction tx = null;
try{
tx = session.beginTransaction();
ContactoProyecto contactoProyecto = new ContactoProyecto();
Proyecto p = (Proyecto)session.get(Proyecto.class, proyecto.getId());
Contacto c = (Contacto)session.get(Contacto.class, contacto.getId());
contactoProyecto.setContacto(c);
contactoProyecto.setProyecto(p);
contactoProyecto.setTipo(tipo);
p.getContactoProyectos().add(contactoProyecto);
session.saveOrUpdate(p);
tx.commit();
}catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
}finally {
session.close();
}
}
Good Tutorial.
I have a problem when recording multiple records.
I get the error
org.hibernate.NonUniqueObjectException:
My code of transaction is:
Session session = sessionFactory.openSession();
Transaction tx = null;
try{
tx = session.beginTransaction();
ContactoProyecto contactoProyecto = new ContactoProyecto();
Proyecto p = (Proyecto)session.get(Proyecto.class, proyecto.getId());
Contacto c = (Contacto)session.get(Contacto.class, contacto.getId());
contactoProyecto.setContacto(c);
contactoProyecto.setProyecto(p);
contactoProyecto.setTipo(tipo);
p.getContactoProyectos().add(contactoProyecto);
session.saveOrUpdate(p);
tx.commit();
}catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
}finally {
session.close();
}
Dear Sir Mykong – you have solved a problem for us! I have a chocolate bar for you here!
Twix, Mars bar, snickers, other brands are available.
Hi, thanks for the tutorial, was really helpful
Hi Sir,
what about an existing stock and category ?
Great tutorial!!!
I’m having difficulty getting this approach (or the one that used @IdClass) working when the entities are audited with Envers. Any thoughts?
Thks dude!!!
You rock Dude, thanks a lot for sharing your knowledge.
I followed the tutorial and works fine.
Hey, thank’s for this example, how to do this with xml mapping ?
thank’s
Is it possible to get all StockCategory objects for Category of consumer (new Category(“CONSUMER”, “CONSUMER COMPANY”)) using Criteria API .
Obviously it should contain the corresponding Stock Objects.
Nice Tutorial and I want to know the use of equals() and hash() function in StockCatagory and StockCatagoryId class
Thank you, u saved my life =D
It works. But when I try to create another association, table stock_market for example, when I already has the table stock_category the hibernate blows up and give lots of errors and doesn’t give any clues to solve the problem.
The Hibernate create the tables but don’t allow me to add data in the table stock_market.
Is there any way I can have an explicit id field for stock_category table. Now the primary key is combination of stockid and categoryid.. So I am not able to put a new row with same stock and category but different date
i dont think people should be using a third class for this. there should be a stock and a category class and one or both can have the extra properties as well as a list of the other class
Nice Article. Find one more.
@ManyToMany in hibernate, creates the association between the entities in such a way that many entities can be associated with many entities. It will be clearer by example. More than one student is associated with more than one teacher and vice-versa. To achieve Many-To-Many association below annotation are used.
http://www.concretepage.com/hibernate/example-manytomany-hibernate
XML mapping would be good here too.
I have a similar setup but see issues when fetching the join table with the two foreign keys.
I have a user table, group table, and a userGroup table with an extra field type.
When fetching the Set of userGroup in group table, I can get the actual group but only the user id and not the content.
When fetching the Set of userGroup in user table, I can get the actual user but only the group id and not the content.
Is anyone having this issue? Ideal is that when fetching the set of userGroup from the group I will be able to get the group object and the user object. Am I missing something in the xml mapping that enables that?
Please make an example of that with XML Mapping.
Following the tutorial, I have created two
entities corresponding to my normal tables, and another for my association table with an embedded id.
To guarantee unicity, I have overridden methods equals and hashCode and the collections are of type Set.
When i persist i’m only getting a null error (see the whole error below), so this gives me no clue about what i’m doing wrong.
Following are the relations of the entities, how they are set and the error.
Entity 1 Prestation :
@OneToMany(fetch=FetchType.LAZY, mappedBy=”id.prestation”, cascade={CascadeType.MERGE, CascadeType.PERSIST})
private Set louers = new HashSet(0);
Entity 2 Materiel :
@OneToMany(fetch=FetchType.LAZY, mappedBy=”id.materiel”)
private Set louers = new HashSet(0);
Entity 3 Louer :
@AssociationOverrides
(
{
@AssociationOverride(name=”id.materiel”, joinColumns = @JoinColumn(name = “ID_MAT”)),
@AssociationOverride(name=”id.prestation”, joinColumns = @JoinColumn(name=”ID_PREST”))
}
)
@Transient
private Materiel materiel;
@Transient
private Prestation prestation;
Embeddable class LouerPK :
@ManyToOne
private Materiel materiel;
@ManyToOne
private Prestation prestation;
Setting :
louer.setMateriel(mat);
louer.setPrestation(prest);
prest.getLouers().add(louer);
Error :
ERROR [model.exception.DAOException] (default task-50) null
Can anyone help?
How can I do the update on the join table? For example if I have 2 relations for stock but with an update I create 3 relations?
the tutorial is very good. Btw me too i need to know how to do the Stock update, updating also the Set StockCategory. I really can’t understand how is it so hard with hibernate / spring mvc doing simple things such this. I think is much much easier use queries like long time ago…
where can i find an example / tutorial for a form with checboxes of a many-to-many relationship?
because i always get “No converter found capable of converting from type java.lang.String to type @javax.persistence.OneToMany java.util.Set”
Hi, Can some one please help me to solve my problem of mapping one to many relationship between primary key and composite key.
http://stackoverflow.com/questions/24609861/hibernate-mapping-one-to-many-relation-ship-between-primary-key-and-composite-ke
how to sort the result by CREATED_DATE using restriction
It saves a lot of time guys, I was having a difficult time with hibernate!! Thanks a lot!
What about an existing stock and existing category ? Thanks
Hi I have one issue with your 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
I used “orphanRemoval=true” from javax.persistence.OneToMany. It works.
@OneToMany(fetch = FetchType.LAZY, mappedBy = “pk.category”, cascade=CascadeType.ALL, orphanRemoval=true)
Thanks for your tutorial! I have a question, How can I do to make an inquiry? I have “stock” and the name “category”. Consultation is with Criteria and meta-models. Help me please!
Dear Mkyong,
thank you for your great article. Depend on your source programs I tried your case 1. I use Netbeans 7.4, MySql 5.6.15, Hibernate 3.6.10.Final. MySQL dont like DESC field, I changed it to DESCRIPTION.
I dont know, what I made wrong, but in case 1 the session.save(stock) not saved data in the middle (stock_category) table. I have no error.
12:28:07,688 INFO [stdout] (http-/127.0.0.1:8080-2) Hibernate:
12:26:51,525 INFO [stdout] (http-/127.0.0.1:8080-2) insert
12:26:51,525 INFO [stdout] (http-/127.0.0.1:8080-2) into
12:26:51,526 INFO [stdout] (http-/127.0.0.1:8080-2) mydb.category
12:26:51,527 INFO [stdout] (http-/127.0.0.1:8080-2) (DESCRIPTION, NAME)
12:26:51,527 INFO [stdout] (http-/127.0.0.1:8080-2) values
12:26:51,528 INFO [stdout] (http-/127.0.0.1:8080-2) (?, ?)
12:28:07,688 INFO [stdout] (http-/127.0.0.1:8080-2) Hibernate:
12:28:07,689 INFO [stdout] (http-/127.0.0.1:8080-2) insert
12:28:07,690 INFO [stdout] (http-/127.0.0.1:8080-2) into
12:28:07,691 INFO [stdout] (http-/127.0.0.1:8080-2) mydb.stock
12:28:07,692 INFO [stdout] (http-/127.0.0.1:8080-2) (STOCK_CODE, STOCK_NAME)
12:28:07,693 INFO [stdout] (http-/127.0.0.1:8080-2) values
12:28:07,694 INFO [stdout] (http-/127.0.0.1:8080-2) (?, ?)
In the Stock and Category tables have rows.
Dou you have any idea?
Thank you
Hello!
Tell me , please.
Why do we need to write “stock.getStockCategories().add(stockCategory);”
in 4. Run it – Case 1? Is it possible just to save stockCategory persistance first, and then stock persistance manually?
Hi,
Sir i like your effort for Doing so many things for us(Developer) and i like your Blog also.But i have one Question Regarding One Example in Hibernate so please help me to Describe that.
My question is that in hibernate https://mkyong.com/hibernate/hibernate-many-to-many-example-join-table-extra-column-annotation/ on this page class Name like : StockCategoryId.java I want to know its hashCode() method has one line which multiply variable result with 31 something like below.
result = 31 * result + (category != null ? category.hashCode() : 0);
My Questions are below
1>I want to know that it does any number i can multiply to result.
2>why u multipy to result with 31.
3>why this method(hashCode()) is necessary.
Eagerly waititng for your replay on this.Thanks in Advance.
Thanks
A hashcode must be a unique identifier for all values inside the object -> if one value changes, the hashcode must change.
If you only add up to numbers, there are multiple cases where different values lead to the same number. For example 6 + 2 = 8, 5 + 3 = 8
If you take a primenumber (31 for example) and mulitply one of the values with it and then add up the second value, there will never be the same output for different values.
6 * 31 + 2 = 188
5 * 31 + 3 = 158
You need to mulitply the hashcode of one object by a primnumber and then add up the second hashcode in order to generate a new unique hashcode that represents the combination of these 2 objects.
Hi,
Do you have a JPA 2.0 example of this use case? It doesn’t seam to work with 2.0 version of the spec.
Thanks
how can we get all the column details of stock_category by primary key?
Could you please help me to get all the column details of stock_category via primary key?
would you please help me figure out how i can join stock with the its category while using Criteria?
Good afternoon Mkyong and coleagues,
I have problems when the extra column is another joinable column, which references to another table.
For instance, in your example in stock_category there is another field which references to stock again
private Stock relatedStock;
@ManyToOne
@JoinColumn(name = “related_stock_id” ,nullable=true)
public Stock getRelatedStock() {
return relatedStock;
}
public void setRelatedStock(Stock relatedStock) {
this.relatedStock = relatedStock;
}
Then, doing a simple main it returns an:
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column relatedStock
Thank you!
In my case, what happens is as if STOCK_ID is a null value when it tries to make the insert into Stock_Category table.
Whatever I try I get the following exception:
org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property […]
at org.hibernate.id.ForeignGenerator.generate(ForeignGenerator.java:101)
at org.hibernate.mapping.Component$ValueGenerationPlan.execute(Component.java:422)
at org.hibernate.id.CompositeNestedGeneratedValueGenerator.generate(CompositeNestedGeneratedValueGenerator.java:121)
at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:117)
at org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:78)
at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:208)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:151)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:843)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:836)
at org.hibernate.ejb.engine.spi.EJB3CascadingAction$1.cascade(EJB3CascadingAction.java:53)
at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:388)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:331)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:209)
at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:418)
at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:358)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:334)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:209)
at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:166)
I’m using a DB2 (AS/400) database.
Hi, I’m working with your tutorial, but I’m facing the same problem that this person in this link http://stackoverflow.com/questions/12970799/hibernate-many-to-many-withe-extracolumn-criteria-problems
Can you please help me about the way to create a criteria for the many-to-many relationship?
Thanks!.
can you solve this problem
its about many to many mapping in hibernate
http://stackoverflow.com/questions/19188751/how-to-prevent-hibernate-to-insert-into-one-table-in-many-to-many-mapping
Thanks a lot.
Why in file StockCategoryId.java in method hashCode in variable result you assign 31* ?
“The value 31 was chosen because it is an odd prime. If it were even and the multiplication overflowed, information would be lost, as multiplication by 2 is equivalent to shifting” Read Effective Java, Second Ed. Chapter 3, Item 9.
you can find some more details in the below link,”http://javadomain.in/hibernate-join-annotation-example/
I have a question :
is there any benefit to declare :
private Set stockCategories
in both classes Stock and Category ?
I think that it will slow down the loading in java bean entities, especially when we have a lot of items in both tables stock and category in database.
thank you.
Hi sir,
My name is siddhant.
I have implemented ur example. But at the time of saving it is throwing an error
Exception in thread “main” java.lang.NullPointerException
/**
package SampleJavaProject.Entity;
import java.io.Serializable;
/**
* @author Siddhant_S
*
*/
@Entity
@Table(name = “person”, schema = “test”)
public class Person implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
@Column(precision = 2)
private double amount;
private Calendar birthDate;
private String street;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = “pk.person”)
private Set bank;
/**
*
*/
public Person() {
}
/**
* @param name
* @param amount
* @param birthDate
* @param street
* @param bank
*/
public Person(String name, double amount, Calendar birthDate, String street) {
this.name = name;
this.amount = amount;
this.birthDate = birthDate;
this.street = street;
this.bank = bank;
}
/**
* @return the id
*/
public int getId() {
return id;
}
/**
* @param id
* the id to set
*/
public void setId(int id) {
this.id = id;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name
* the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the amount
*/
public double getAmount() {
return amount;
}
/**
* @param amount
* the amount to set
*/
public void setAmount(double amount) {
this.amount = amount;
}
/**
* @return the birthDate
*/
public Calendar getBirthDate() {
return birthDate;
}
/**
* @param birthDate
* the birthDate to set
*/
public void setBirthDate(Calendar birthDate) {
this.birthDate = birthDate;
}
/**
* @return the street
*/
public String getStreet() {
return street;
}
/**
* @param street
* the street to set
*/
public void setStreet(String street) {
this.street = street;
}
/**
* @return the bank
*/
public Set getBank() {
return bank;
}
/**
* @param bank
* the bank to set
*/
public void setBank(Set bank) {
this.bank = bank;
}
}
/**
package SampleJavaProject.Entity;
import java.io.Serializable;
/**
* @author Siddhant_S
*
*/
@Entity
@Table(name = “Bank”, schema = “test”)
public class Bank implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
@Column(precision = 2)
private double amount;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = “pk.bank”)
private Set associations;
/**
*
*/
public Bank() {
}
/**
* @param name
* @param amount
* @param associations
*/
public Bank(String name, double amount) {
this.name = name;
this.amount = amount;
}
/**
* @return the id
*/
public int getId() {
return id;
}
/**
* @param id
* the id to set
*/
public void setId(int id) {
this.id = id;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name
* the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the amount
*/
public double getAmount() {
return amount;
}
/**
* @param amount
* the amount to set
*/
public void setAmount(double amount) {
this.amount = amount;
}
/**
* @return the associations
*/
public Set getAssociations() {
return associations;
}
/**
* @param associations
* the associations to set
*/
public void setAssociations(Set associations) {
this.associations = associations;
}
}
/**
package SampleJavaProject.Entity;
import java.io.Serializable;
/**
* @author Siddhant_S
*
*/
@Entity
@Table(name = “Person_Bank_Association”, schema = “test”)
@AssociationOverrides({
@AssociationOverride(name = “pk.person”, joinColumns = @JoinColumn(name = “person_id”)),
@AssociationOverride(name = “pk.bank”, joinColumns = @JoinColumn(name = “bank_id”)) })
public class PersonBankAssociation implements Serializable {
@EmbeddedId
private PersonBankId pk;
private String name;
private double amount;
/**
*
*/
public PersonBankAssociation() {
}
/**
* @param name
* @param amount
*/
public PersonBankAssociation(String name, double amount) {
this.name = name;
this.amount = amount;
}
/**
* @return the pk
*/
public PersonBankId getPk() {
return pk;
}
/**
* @param pk
* the pk to set
*/
public void setPk(PersonBankId pk) {
this.pk = pk;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name
* the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the amount
*/
public double getAmount() {
return amount;
}
/**
* @param amount
* the amount to set
*/
public void setAmount(double amount) {
this.amount = amount;
}
/**
*
* @return person
*/
@Transient
public Person getPerson() {
return getPk().getPerson();
}
public void setPerson(Person person) {
getPk().setPerson(person);
}
/**
*
* @return bank
*/
@Transient
public Bank getBank() {
return getPk().getBank();
}
public void setBank(Bank bank) {
getPk().setBank(bank);
}
}
/**
package SampleJavaProject.Entity;
import java.io.Serializable;
/**
* @author Siddhant_S
*
*/
@Embeddable
public class PersonBankId implements Serializable {
@ManyToOne
private Person person;
@ManyToOne
private Bank bank;
/**
*
*/
public PersonBankId() {
}
/**
* @param person
* @param bank
*/
public PersonBankId(Person person, Bank bank) {
this.person = person;
this.bank = bank;
}
/**
* @return the person
*/
public Person getPerson() {
return person;
}
/**
* @param person
* the person to set
*/
public void setPerson(Person person) {
this.person = person;
}
/**
* @return the bank
*/
public Bank getBank() {
return bank;
}
/**
* @param bank
* the bank to set
*/
public void setBank(Bank bank) {
this.bank = bank;
}
}
/**
package SampleJavaProject.HibernateTransaction;
import java.util.Calendar;
/**
* @author Siddhant_S
*
*/
public class PersonBankApp {
private static final Logger _logger = LoggerFactory
.getLogger(PersonBankApp.class);
private static final Session session = HibernateUtil.getSessionFactory()
.openSession();
/**
* @return the Logger
*/
public static Logger getLogger() {
return _logger;
}
/**
* @return the session
*/
public static Session getSession() {
return session;
}
/**
* @param args
*/
public static void main(String[] args) {
getSession().beginTransaction();
Bank bank=new Bank(“SBI”, 2000.00);
getSession().save(bank);
Calendar birthDate=GregorianCalendar.getInstance();
birthDate.setTime(new Date());
Person person=new Person(“Siddhant”, 2000.00, birthDate, “lane4”);
PersonBankAssociation associations=new PersonBankAssociation(“Sbi name”, 2000.00);
associations.setPerson(person);
associations.setBank(bank);
person.getBank().add(associations);
Integer personId= (Integer) getSession().save(person);
getSession().getTransaction().commit();
getSession().close();
System.out.println(“Person id:”+personId);
}
}
Please help me on this code i m not able to save the data in the DB.
Please help me as soon as possible
I got my solution for this problem.
Actually it is because of the the initializations problem.
Hi Siddhant, please elaborate on your answer, I believe I am getting the same issue as you. Thank you very much.
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
thanks a lot..this really helped..
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 🙂
How i can delete the association?
Same problem here, how to delete the association ?
I have the same issue. I need to delete the association… i tried to remove my StockCategory equivalent from collection on the Stock side and nothing happens.. i tried to remove directly the StockCategory using the entity manager and nothing happens…
You need to update both sides of the link between Load and Session:
Session session = sessionDao.getObject(sessionId);
Load load = loadDao.getObject(loadId);
load.getSessions().remove(session);
session.getLoads().remove(load);
loadDao.saveObject(load);
Actually, many developer use defensive methods to manage bi-directional associations. For example on Load, you could add the following methods:
public void removeFromSessions(Session session) {
this.getSessions().remove(session);
session.getLoads().remove(this);
}
public void addToSessions(Session session) {
this.getSessions().add(session);
session.getLoads().add(this);
}
Excellent tutorial, can you please write a tutorail with same example but using XML mappings
Thanks a lot
Mazhar Hassan
I’ve researched. You can go to book “Hibernate In Action” page 229. It exposes how to do that.
There are 3 classes: Category, Item and CategorizedItem
Main things to do:
public class CategorizedItem {
private User user;
private Date dateAdded;
private Item item;
private Category category;
….
}
In file mapping Category.hbm.xml
….
Same thing i was developed for sql but it’s creating an third table with raw datatype stock
Hi Mk,
While updating association of Stock_Category, older record were not deleted.
They exist with new records in STCOK_CATEGORY table.
Thanks a lot for the example Mkyong!
It been worked with also EntityManager!
Cheers.
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.
@Manoj for xml mapping view this:
http://www.skill-guru.com/blog/2010/02/10/mapping-composite-keys-in-hibernate/
hope it will help!!
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={"human","qualification"}), name="myUniqueConstraint") 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:
Hope this simplifies the solution
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!!
nice explanation
Awesome, it works! Thank you!
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 ….
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
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
Hey..How did you resolve this error?
hi chandan ,
I am facing the same issue , can you please how to solve it
thanks.
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();
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,
This is probably a better/nicer way to do this: http://stackoverflow.com/questions/5127129/mapping-many-to-many-association-table-with-extra-columns
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!
Hi,
Could you can provide the same example using xml mapping file?
thanks,
Pradeep
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.
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.
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
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
Where is the tutorial? I see only a bunch of unreadable code. What do those lines of code mean?
this tuto is to great, i transform it with attribute override to make one to many association with extra columns
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 😉
I suggest logging a bug with Hibernate on this
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
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
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;
}
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
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
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!
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!
Well I got the assocition, but how query a extra field from de join class using Criteria?
Thanks for your help
How do you persist a stockCategory instance getting an existing stock and an existing category?
Thanks in advance!
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
Fetch and assign works as expected.
Thanks!
Got it. Had to delete my association explicitly
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="STOCK_CATEGORY") 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
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("7052"); stock.setStockName("PADINI"); manager.persist(stock); Category category1 = new Category("CONSUMER", "CONSUMER COMPANY"); manager.persist(category1); StockCategory stockCategory = new StockCategory(); stockCategory.setCreatedDate(new Date()); stockCategory.setCreatedBy("system"); 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("Done");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 = "pk.stock", cascade=CascadeType.ALL) public Set<StockCategory> 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:
With your solution it’s:
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
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.
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?
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 🙂
I have the same problem. Did you solve the problem?
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)
I have exactly the same issue in hibernate4. Were you able to resolve this?
I have the same issue and can’t found any solution on that
Hello,
I was looking at Case1 and I don’t see where category1.stockCategories is being updated.
Maybe I am missing something ?
Thanks
John
Useful example for many to many
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
Can you please help me to solve this issue
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.
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
I didn’t test it with XML mapping, caused quite complicated to implement this in XML way, is the annotation version working?
yes, that was fine. But I need to work with xml mapping and i could not found anywhere on google 🙁
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 🙂
The Xml Mapping worked only if we do individual save like stock,category followed by StockCategory.
session.beginTransaction(); Stock stock = new Stock(); stock.setStockCode("7052"); stock.setStockName("PADINI"); stock.setStockId(10); session.save(stock); Category category1 = new Category(11,"CONSUMER", "CONSUMER COMPANY"); // Category category2 = new Category(12,"INVESTMENT", "INVESTMENT COMPANY"); session.save(category1); StockCategory stockcat=new StockCategory(); stockcat.setStock(stock); stockcat.setCategory(category1); stockcat.setCreatedBy("me"); 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();Can you please tell me which tutorial?
I need xml mapping for many-to-many with extra columns.
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
Is this compile error? or runtime error?
Exampled tested in Hibernate 3.6, Eclipse 3.6, Maven3 and JDK1.6
It is a compile error.
Eclipse 3.7, JDK 1.6, Hibernate-JPA 2.0
At least post your compile error message for debug 🙂
Errors are listed on my first post 🙂
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
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:
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!
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
annotation above the method or attribute, so make sure you only stick to one access strategy. 🙂
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.
Should be JPA annotation in Hibernate project
What id stock_category table has one more column stockcategoryid as primary key??