-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 
Author Message
 Post subject: Prevent cascade update from deleting all child rows first?
PostPosted: Mon Jul 09, 2007 8:47 am 
Newbie

Joined: Mon Jul 09, 2007 8:33 am
Posts: 3
Hi,
I am facing a problem in Hibernate cascade update. I have a Customer with a mapping set of Pieces (with CustomerPiece-mapping in the middle)

Customer.hbm.xml:
...
...
<set name="pieces" table="customer_piece" lazy="true">
<key column="customer_id" />
<many-to-many column="piece_id" class="fi.saha.hibernate.Piece" />
</set>

CustomerPiece.hbm.xml:
...
...
<many-to-one name="customer" class="fi.saha.hibernate.Customer" insert="false" update="false" column="customer_id" />

So the DB-structure goes like customer=>customer_piece=>piece.

When I try to update the customer instance with a new set of Pieces, Hibernate first deletes all the rows from customer_piece -table and then inserts them again (see the generated SQL below).
CustomerPiece -table holds valuable information for our software and thus all the rows must not be deleted from that table at the beginning of the update.

How can I prevent Hibernate from deleting all the rows at first from "customer_piece" in the cascade update and instead only delete those not in the updated Set and add the new ones?


regards,
Jari Laitinen


Hibernate version:
3.0

Name and version of the database you are using:
MySQL 5.0

The generated SQL (show_sql=true):
15:29:23,484 INFO [STDOUT] Hibernate: update customer set NAME=?, PASSWORD=?, PIECE_LIMIT=?, DATABASE_NAME=?, ACTIVE=?, LAST_VISIT=?, TYPE=?, START_DATE=?, END_DATE=?, CUSTOMER=?, VERSION=?, CONTACT=?, STREETADDRESS=?, POSTALADDRESS=?, INFO=?, INFO2=?, GSM=?, PHONE=?, EMAIL=?, ONLINE_ORDER=?, LOGGED=?, DATE_UPDATED=?, UPDATE_FLAG=?, EXTRANET_PASSWORD=?, HALT_FLAG=? where ID=?
15:29:23,484 INFO [STDOUT] Hibernate: delete from customer_piece where customer_id=?
15:29:23,500 INFO [STDOUT] Hibernate: insert into customer_piece (customer_id, piece_id) values (?, ?)
etc...
etc...


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 09, 2007 2:19 pm 
Newbie

Joined: Mon Jul 02, 2007 2:12 pm
Posts: 18
Hi Jari,

to me it seems your association should be many-to-many ;-)

In your mapping the CustomerPiece table has to primary key (id), so how should Hibernate do tupel operations? You map a set and Hibernate does a set operation (delete all, insert). You can add id column to CustomerPiece table and tell Hibernate that there is one (<idbag>).

But maybe many-to-many is your choice.

-Thomas


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 09, 2007 2:21 pm 
Newbie

Joined: Mon Jul 02, 2007 2:12 pm
Posts: 18
Hi Jari,

to me it seems your association should be many-to-many ;-)

In your mapping the CustomerPiece table has to primary key (id), so how should Hibernate do tupel operations? You map a set and Hibernate does a set operation (delete all, insert). You can add id column to CustomerPiece table and tell Hibernate that there is one (<idbag>).

But maybe many-to-many is your choice.

-Thomas


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jul 12, 2007 10:43 am 
Newbie

Joined: Mon Jul 09, 2007 8:33 am
Posts: 3
Hi Thomas and thanks for your reply although I didn't quite get it ;)

Below you can find the full mapping of the CustomerPiece.hbm.xml

Could there be a way to tell Hibernate to only add the missing rows to db and delete the rows from db not found in the Set of Piece-instances when updating the Customer directly?
(Customer.hbm.xml was in the first post of this topic)


<class
name="fi.saha.hibernate.CustomerPiece"
table="customer_piece"
>
<id name="id"
type="java.lang.Integer"
column="id">
<generator class="native" />
</id>
<property
name="pieceId"
type="int"
column="piece_id"
not-null="true"
length="11"/>

<property
name="customerId"
type="int"
column="customer_id"
not-null="true"
length="11"
/>

....
... some properties here in between not related to the problem but
... contain valuable information to the software
....

<!-- Associations -->

<many-to-one name="customer" class="fi.saha.hibernate.Customer" insert="false" update="false" column="customer_id" />
<many-to-one name="piece" class="fi.soittorasia.saha.hibernate.Piece" insert="false" update="false" column="piece_id"/>


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 13, 2007 4:19 am 
Newbie

Joined: Mon Jul 02, 2007 2:12 pm
Posts: 18
Hi Jari,

that's exactly was Hibernate does with many-to-many:

10:13:23,546 DEBUG org.hibernate.SQL: delete from customer_piece where customer_id=? and piece_id=?
Hibernate: delete from customer_piece where customer_id=? and piece_id=?
10:13:23,546 DEBUG org.hibernate.jdbc.AbstractBatcher: preparing statement
10:13:23,562 DEBUG org.hibernate.type.LongType: binding '1' to parameter: 1
10:13:23,562 DEBUG org.hibernate.type.LongType: binding '3' to parameter: 2
10:13:23,562 DEBUG org.hibernate.persister.collection.AbstractCollectionPersister: done deleting collection rows: 1 deleted

As you can see, after deleting one piece from Set, Hibernate deletes the tupel from relation table customer_piece.

Here is a complete sample:


Customer.java:

package forum.jari;

import java.util.HashSet;
import java.util.Set;

public class Customer {
private long id;
private String name;
private Set<Piece> pieces = new HashSet<Piece>();


public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Piece> getPieces() {
return pieces;
}
public void setPieces(Set<Piece> pieces) {
this.pieces = pieces;
}



}


Piece.java:

package forum.jari;

public class Piece {
private long id;
private String name;



public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}

}


JariMain.java:

package forum.jari;

import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;

public class JariMain {

private SessionFactory factory;

public static void main(String[] args) throws Exception {

final JariMain jari = new JariMain();

Configuration cfg = new Configuration()
.configure()
.addClass(Customer.class)
.addClass(Piece.class)
.setProperty(Environment.HBM2DDL_AUTO, "create");

cfg.setProperty("hibernate.show_sql", "true");


jari.factory = cfg.buildSessionFactory();

jari.createData();

jari.listData();

jari.changeSet();

}

private void changeSet() {

Session s = factory.openSession();
Transaction tx = s.beginTransaction();

List<Customer> list = s.createCriteria(Customer.class).list();
for (Customer c : list) {
System.out.println("Change Set of pieces for Customer: " + c.getName());
Set pieces = c.getPieces();
Iterator it = pieces.iterator();
it.next(); // goto first item
it.remove(); // remove it
System.out.println("***");
}

tx.commit();
s.close(); // we'll be save now


}

private void listData() {
Session s = factory.openSession();
Transaction tx = s.beginTransaction();

List<Customer> list = s.createCriteria(Customer.class).list();
for (Customer c : list) {
System.out.println("Customer: " + c.getName());
for (Piece p : c.getPieces()) {
System.out.println(" Piece: " + p.getId() + " , " + p.getName());
}
System.out.println("---");
}

tx.commit();
s.close();

}

private void createData() {
Session s = factory.openSession();
Transaction tx = s.beginTransaction();

Customer c = new Customer();
c.setName("Customer1");


Piece p1 = new Piece();
p1.setName("Piece1");
c.getPieces().add(p1);

Piece p2 = new Piece();
p2.setName("Piece2");
c.getPieces().add(p2);

s.save(c);

tx.commit();
s.close();

}


}

Customer.hbm.xml:

<?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
package="forum.jari">

<class name="Customer" table="customer" lazy="true">
<comment>Customer</comment>

<id name="id">
<generator class="native"/>
</id>

<property name="name"
not-null="true"
length="80"
column="name"/>


<set name="pieces" table="customer_piece" lazy="true" cascade="save-update">
<key column="customer_id" />
<many-to-many column="piece_id" class="Piece" />
</set>


</class>

</hibernate-mapping>


Piece.hbm.xml:

<?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
package="forum.jari">

<class name="Piece" table="piece" lazy="true">
<comment>Piece</comment>

<id name="id">
<generator class="native"/>
</id>


<property name="name"
not-null="true"
length="80"
column="name"/>


</class>

</hibernate-mapping>



-Thomas


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 13, 2007 4:58 am 
Newbie

Joined: Mon Jul 09, 2007 8:33 am
Posts: 3
Thanks very much for your testing!

So I guess the main point here is that I should always modify the existing Set of Pieces in the Customer instance and never set the entire Set of Pieces at once if I want Hibernate to only add the added ones and remove the removed ones.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.