Hi!
I have problems with a many-to-many relationship between some products and categories.
My database tables are (I am using MySQL):
CREATE TABLE tbl_category (
id_category int auto_increment not null,
name varchar(50) not null,
description varchar(255) not null,
PRIMARY KEY (id_category),
INDEX name (name)
);
CREATE TABLE tbl_product (
id_product int auto_increment not null,
name varchar(200) not null,
description varchar(255) not null,
price float not null,
PRIMARY KEY (id_product),
INDEX name (name)
);
CREATE TABLE tbl_category_product (
id_category int not null,
id_product int not null,
INDEX category (id_category),
INDEX product (id_product)
);
My bibernate mapping file is:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping auto-import="true" default-lazy="false">
<class name="business.Category" table="TBL_CATEGORY">
<id name="id" column="id_category" unsaved-value="-1">
<generator class="native"/>
</id>
<property name="name" column="name" not-null="true"/>
<property name="description" column="description" not-null="true"/>
<set name="products" table="TBL_CATEGORY_PRODUCT" inverse="true" fetch="join">
<key column="id_category"/>
<many-to-many column="id_product" class="business.Product" fetch="join" unique="true"/>
</set>
</class>
<class name="business.Product" table="TBL_PRODUCT">
<id name="id" column="id_product" unsaved-value="-1">
<generator class="native"/>
</id>
<property name="name" column="name"/>
<property name="description" column="description"/>
<property name="price" column="price"/>
<set name="categories" table="TBL_CATEGORY_PRODUCT" inverse="false" fetch="join">
<key column="id_product"/>
<many-to-many column="id_category" class="business.Category" fetch="join" unique="true"/>
</set>
</class>
</hibernate-mapping>
This is where I save a new product and i edit an existing one:
public class HibernateShop extends HibernateDaoSupport {
...
public Category loadCategory(long id) throws DataAccessException {
return (Category) getHibernateTemplate().load(Category.class, new Long(id));
}
public void storeProduct(Product product) throws DataAccessException {
long[] categoriesIn = product.getCategoriesIn(); //these come from the multi selectable combo
if (product.getId() == -1) { // add
if (categoriesIn != null) {
for (int i = 0; i < categoriesIn.length; i++) {
product.getCategories().add(loadCategory(categoriesIn[i]));
}
}
getHibernateTemplate().save(product);
} else { //edit
if (categoriesIn != null) {
for (int i = 0; i < categoriesIn.length; i++) {
product.getCategories().add(loadCategory(categoriesIn[i]));
}
}
getHibernateTemplate().update(product);
}
}
...
}
Everything works fine when I add a new product. BUT WHEN A EDIT AN EXISTING ONE I GET A NASTY EXCEPTION:
org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: null
at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:626)
at org.springframework.orm.hibernate3.HibernateAccessor.convertHibernateAccessException(HibernateAccessor.java:229)
at org.springframework.orm.hibernate3.HibernateTemplate.execute(HibernateTemplate.java:316)
at org.springframework.orm.hibernate3.HibernateTemplate.persist(HibernateTemplate.java:642)
at HibernateShop.storeProduct(HibernateShop.java:102)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
...
public class Entity {
private Long id;
public long getId() {
return (id != null) ? id.longValue() : -1;
}
public void setId(long id) {
this.id = new Long(id);
}
public boolean isNew() {
return (id == null);
}
}
public class Category extends Entity implements Serializable {
private String name;
private String description;
private long creationDate;
private Set products = new HashSet();
public Category() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public long getCreationDate() {
return creationDate;
}
public void setCreationDate(long creationDate) {
this.creationDate = creationDate;
}
public Set getProducts() {
return products;
}
public void setProducts(Set products) {
this.products = products;
}
}
public class Product extends Entity implements Serializable {
private String name;
private String description;
private float price;
private long creationDate;
private long[] categoriesIn;
private Collection allCategories;
private Set categories = new HashSet();
public Product() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
public long getCreationDate() {
return creationDate;
}
public void setCreationDate(long creationDate) {
this.creationDate = creationDate;
}
public long[] getCategoriesIn() {
return categoriesIn;
}
public void setCategoriesIn(long[] categoriesIn) {
this.categoriesIn = categoriesIn;
}
public Collection getAllCategories() {
return allCategories;
}
public void setAllCategories(Collection allCategories) {
this.allCategories = allCategories;
}
public Set getCategories() {
return categories;
}
public void setCategories(Set categories) {
this.categories = categories;
}
}