I have a many to many relation, map as the DATABASE SCHEMA (see below) describes.
In the database, I have three tables, one for clients, one for products, the last one (called sells) is to implement the MxN relation among clients and products.
In the java side, I have also three classes to map the above three tables. See JAVA DEFINITION CODE.
I map the middle class with a composite id where each id is a key-many-to-one, that targets to the associated class (products and clients). The mapping is described in XML DEFINITION CODE.
The way I work adding and removing sells to the client is the following:
Adding sells:
Code:
ClientBean client = new ClientBean("Albert");
session.saveOrUpdate(client);
//article
ProductBean article = new ProductBean("headache");
article.setDescription("headache");
session.save(article);
//Sells
Sells sells = new Sells(article,client,"11-11-1111");
client.getSells().add(sells);
In this case I create a client and a product, just for the example, this creation it will not be needed later. To create the relation, I create a sells element, initializing the article and client, and I just add the new sell to the client. Hibernate will create a row in the relation table.
I'm really new in hibernate and I don't know if this is the best way to work with NxM relations, any comments? , working like this is quite comfortable.
The main problem is deleting the elements.
This is what I do:
Code:
Query query = session.createQuery("from Sells s where s.id.client.name = ?");
query.setString(0, "Albert");
Iterator articlesFromClient = query.list().iterator();
while (articlesFromClient.hasNext()) {
Sells object = (Sells) articlesFromClient.next();
System.out.println(object.getId().getProduct().getDescription());
articlesFromClient.remove();
}
What I do to delete is query all the relations and delete the relation among the client and the product.
Working like this is not really comfortable, but is the only way, until now, I found to delete this relation.
What I would like to do is just remove the element from the client collection and let hibernate delete the relation, see following code:
Code:
Query query = session.createQuery("from ClientBean c where c.name = ?");
query.setString(0, "Albert");
ClientBean c = (ClientBean) query.uniqueResult();
//session.delete(c);
Iterator<Sells> sellsIterator = c.getSells().iterator();
while (sellsIterator.hasNext()) {
Sells sells2 = (Sells) sellsIterator.next();
System.out.println(sells2.getData());
sellsIterator.remove();
}
The problem with the above approach is that when it is executed, hibernate, instead of deleting the relation with the client tries to update the it, telling
to the database that row has a field null. The database tells hibernate that this update is not possible due to this field is part of a primary key and can't be null.
Here is the error:
Hibernate: update CLIENTS_has_PRODUCTS set CLIENT_ID=null where CLIENT_ID=?
15:52:40,476 WARN JDBCExceptionReporter:77 - SQL Error: 1048, SQLState: 23000
15:52:40,476 ERROR JDBCExceptionReporter:78 - Column 'CLIENT_ID' cannot be null
Any solutions to force hibernate to delete the row instead of updating it?.(I already tried to remove the relation from client and product and after commit, but same error)
Sorry for this long and boring explanation.
ADDITIONAL CODE:
DATABASE SCHEMA:
Code:
DROP DATABASE IF EXISTS sells;
CREATE DATABASE sells;
USE sells;
CREATE TABLE PRODUCT (
ID INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
DESCRIPTION Varchar(45) NULL,
PRIMARY KEY(ID)
);
CREATE TABLE CLIENT (
ID INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
NAME Varchar(45) NULL,
PRIMARY KEY(ID)
);
CREATE TABLE CLIENTS_has_PRODUCTS (
CLIENT_ID INTEGER UNSIGNED,
PRODUCT_ID INTEGER UNSIGNED,
DATA Varchar(45) NULL,
PRIMARY KEY(CLIENT_ID,PRODUCT_ID),
FOREIGN KEY(CLIENT_ID)
REFERENCES CLIENT(ID),
FOREIGN KEY(PRODUCT_ID)
REFERENCES PRODUCT(ID)
);
JAVA DEFINITION CODE:
Code:
public class ClientBean {
private int id=0;
private String name;
private Set sells; //Objects Type hibernate_beans.Sells
public ClientBean(String name) {
super();
this.name = name;
this.sells = new HashSet();
}
...getters and setter...
}
public class ProductBean {
private int id=0;
private String description;
private Set sells; //Objects Type hibernate_beans.Sells
public ProductBean(String description) {
this.description = description;
this.sells = new HashSet();
}
public ProductBean() {
}
...getters and setters....
}
public class Sells implements Serializable{
public static class SellsID implements Serializable{
private ProductBean product=null;
private ClientBean client=null;
public SellsID(){}
public SellsID(ProductBean product, ClientBean client) {
super();
this.product = product;
this.client = client;
}
...getters and setters....
}
private SellsID id = new SellsID();
public Sells(){}
public Sells(ProductBean article, ClientBean client, String data) {
super();
this.id = new SellsID(article,client);
this.data = data;
}
private String data = null;
@Override
public boolean equals(Object obj) {
if(obj instanceof Sells){
Sells s = (Sells) obj;
if((s.getId().getProduct().getId() == this.getId().getProduct().getId())
&&
(s.getId().getClient().getId() == this.getId().getClient().getId())
) return true;
else
return false;
}else return false;
....getters and setters....
}
XML DEFINITION CODE
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping default-lazy="false">
<!--ClientBean-->
<class name="hibernate_beans.ClientBean" table="CLIENT">
<id name="id" type="int" column="ID" unsaved-value="0">
<generator class="native"/>
</id>
<property name="name" type="string" column="NAME"/>
<set name="sells" inverse="false" lazy="true" cascade="all">
<key column="CLIENT_ID"/>
<one-to-many class="hibernate_beans.Sells"/>
</set>
</class>
<!--ArticleBean-->
<class name="hibernate_beans.ProductBean" table="PRODUCT">
<id name="id" type="int" column="ID" unsaved-value="0">
<generator class="native"/>
</id>
<property name="description" type="string" column="DESCRIPTION"/>
<set name="sells" inverse="false" lazy="true" cascade="all">
<key column="PRODUCT_ID"/>
<one-to-many class="hibernate_beans.Sells"/>
</set>
</class>
<class name="hibernate_beans.Sells" table="CLIENTS_has_PRODUCTS">
<composite-id name="id" class="hibernate_beans.Sells$SellsID">
<key-many-to-one name="product" class="hibernate_beans.ProductBean" column="PRODUCT_ID" access="field"/>
<key-many-to-one name="client" class="hibernate_beans.ClientBean" column="CLIENT_ID" access="field"/>
</composite-id>
<property name="data" type="string" column="DATA"/>
</class>
</hibernate-mapping>