Hibernate – Cascade example (save, update, delete and delete-orphan)
Cascade is a convenient feature to save the lines of code needed to manage the state of the other side manually.
The “Cascade” keyword is often appear on the collection mapping to manage the state of the collection automatically. In this tutorials, this one-to-many example will be used to demonstrate the cascade effect.
Cascade save / update example
In this example, if a ‘Stock’ is saved, all its referenced ‘stockDailyRecords’ should be saved into database as well.
1. No save-update cascade
In previous section, if you want to save the ‘Stock’ and its referenced ‘StockDailyRecord’ into database, you need to save both individually.
Stock stock = new Stock(); StockDailyRecord stockDailyRecords = new StockDailyRecord(); //set the stock and stockDailyRecords data stockDailyRecords.setStock(stock); stock.getStockDailyRecords().add(stockDailyRecords); session.save(stock); session.save(stockDailyRecords);
Output
Hibernate:
insert into mkyong.stock (STOCK_CODE, STOCK_NAME)
values (?, ?)
Hibernate:
insert into mkyong.stock_daily_record
(STOCK_ID, PRICE_OPEN, PRICE_CLOSE, PRICE_CHANGE, VOLUME, DATE)
values (?, ?, ?, ?, ?, ?)2. With save-update cascade
The cascade=”save-update” is declared in ‘stockDailyRecords’ to enable the save-update cascade effect.
<!-- Stock.hbm.xml --> <set name="stockDailyRecords" cascade="save-update" table="stock_daily_record"...> <key> <column name="STOCK_ID" not-null="true" /> </key> <one-to-many class="com.mkyong.common.StockDailyRecord" /> </set>
Stock stock = new Stock(); StockDailyRecord stockDailyRecords = new StockDailyRecord(); //set the stock and stockDailyRecords data stockDailyRecords.setStock(stock); stock.getStockDailyRecords().add(stockDailyRecords); session.save(stock);
Output
Hibernate:
insert into mkyong.stock (STOCK_CODE, STOCK_NAME)
values (?, ?)
Hibernate:
insert into mkyong.stock_daily_record
(STOCK_ID, PRICE_OPEN, PRICE_CLOSE, PRICE_CHANGE, VOLUME, DATE)
values (?, ?, ?, ?, ?, ?)The code session.save(stockDailyRecords); is no longer required, when you save the ‘Stock’, it will “cascade” the save operation to it’s referenced ‘stockDailyRecords’ and save both into database automatically.
Cascade delete example
In this example, if a ‘Stock’ is deleted, all its referenced ‘stockDailyRecords’ should be deleted from database as well.
1. No delete cascade
You need to loop all the ‘stockDailyRecords’ and delete it one by one.
Query q = session.createQuery("from Stock where stockCode = :stockCode "); q.setParameter("stockCode", "4715"); Stock stock = (Stock)q.list().get(0); for (StockDailyRecord sdr : stock.getStockDailyRecords()){ session.delete(sdr); } session.delete(stock);
Output
Hibernate:
delete from mkyong.stock_daily_record
where DAILY_RECORD_ID=?
Hibernate:
delete from mkyong.stock
where STOCK_ID=?2. With delete cascade
The cascade=”delete” is declared in ‘stockDailyRecords’ to enable the delete cascade effect. When you delete the ‘Stock’, all its reference ‘stockDailyRecords’ will be deleted automatically.
<!-- Stock.hbm.xml --> <set name="stockDailyRecords" cascade="delete" table="stock_daily_record" ...> <key> <column name="STOCK_ID" not-null="true" /> </key> <one-to-many class="com.mkyong.common.StockDailyRecord" /> </set>
Query q = session.createQuery("from Stock where stockCode = :stockCode "); q.setParameter("stockCode", "4715"); Stock stock = (Stock)q.list().get(0); session.delete(stock);
Output
Hibernate:
delete from mkyong.stock_daily_record
where DAILY_RECORD_ID=?
Hibernate:
delete from mkyong.stock
where STOCK_ID=?Cascade delete-orphan example
In above cascade delete option, if you delete a Stock , all its referenced ‘stockDailyRecords’ will be deleted from database as well. How about if you just want to delete two referenced ‘stockDailyRecords’ records? This is called orphan delete, see example…
1. No delete-orphan cascade
You need to delete the ‘stockDailyRecords’ one by one.
StockDailyRecord sdr1 = (StockDailyRecord)session.get(StockDailyRecord.class, new Integer(56)); StockDailyRecord sdr2 = (StockDailyRecord)session.get(StockDailyRecord.class, new Integer(57)); session.delete(sdr1); session.delete(sdr2);
Output
Hibernate:
delete from mkyong.stock_daily_record
where DAILY_RECORD_ID=?
Hibernate:
delete from mkyong.stock_daily_record
where DAILY_RECORD_ID=?2. With delete-orphan cascade
The cascade=”delete-orphan” is declared in ‘stockDailyRecords’ to enable the delete orphan cascade effect. When you save or update the Stock, it will remove those ‘stockDailyRecords’ which already mark as removed.
<!-- Stock.hbm.xml --> <set name="stockDailyRecords" cascade="delete-orphan" table="stock_daily_record" > <key> <column name="STOCK_ID" not-null="true" /> </key> <one-to-many class="com.mkyong.common.StockDailyRecord" /> </set>
StockDailyRecord sdr1 = (StockDailyRecord)session.get(StockDailyRecord.class, new Integer(56)); StockDailyRecord sdr2 = (StockDailyRecord)session.get(StockDailyRecord.class, new Integer(57)); Stock stock = (Stock)session.get(Stock.class, new Integer(2)); stock.getStockDailyRecords().remove(sdr1); stock.getStockDailyRecords().remove(sdr2); session.saveOrUpdate(stock);
Output
Hibernate:
delete from mkyong.stock_daily_record
where DAILY_RECORD_ID=?
Hibernate:
delete from mkyong.stock_daily_record
where DAILY_RECORD_ID=?In short, delete-orphan allow parent table to delete few records (delete orphan) in its child table.
How to enable cascade ?
The cascade is supported in both XML mapping file and annotation.
1. XML mapping file
In XML mapping file, declared the cascade keyword in your relationship variable.
<!-- Stock.hbm.xml --> <set name="stockDailyRecords" cascade="save-update, delete" table="stock_daily_record" ...> <key> <column name="STOCK_ID" not-null="true" /> </key> <one-to-many class="com.mkyong.common.StockDailyRecord" /> </set>
2. Annotation
In annotation, declared the CascadeType.SAVE_UPDATE (save, update) and CascadeType.REMOVE (delete) in @Cascade annotation.
//Stock.java @OneToMany(mappedBy = "stock") @Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE}) public Set<StockDailyRecord> getStockDailyRecords() { return this.stockDailyRecords; }
Further study – Cascade – JPA & Hibernate annotation common mistake.
inverse vs cascade
Both are totally different notions, see the differential here.
Conclusion
Cascade is a very convenient feature to manage the state of the other side automatically. However this feature come with a price, if you do not use it wisely (update or delete), it will generate many unnecessary cascade effects (cascade update) to slow down your performance, or delete (cascade delete) some data you didn’t expected.
All these cascade operations works with only Bi-directional associations, right? or It can support unidirectional associations? What about inverse then…I think that should work with Bidirectional.
Thank you
Hi
your article is too good . very easy to undersatnd and get the basics right.
i have a requiremnt where my StockDetail extend my stock class and when i say
session.save(StockDetail ); which is my child class
a record should be inserted into parent table(Stock) and then insertion should happen in my child table(StockDetail)
Thanks indeed!It was simple and great!
It helped me a lot!
See ya in the future!
Thanks, it’s very easy to understand the cascade from your article!
Thanks for your excellent work. I am confused with your delete-orphan demo:
if Stock side is not ownership (inverse=”false”), remove StockDailyRecord from its list will only lead to set StockDailyRecord.STOCK_ID to be null but won’t remove StockDailyRecord from DB.
What’s more:
1) I am using Hibernate 3.2.6
2) if I set many-to-one(insert=”false” update=”false”) in StockDailyRecord side. delete-orphan works properly.
[...] It will inserted or updated the record into STOCK table and call another insert or update statement (cascade=”save-update”) on StockDailyRecord. More detail example here… [...]
I liked your article on fetch strategies but this one, imo, could be improved for explaining delete-orphan. I found the following better: http://docs.jboss.org/hibernate/core/3.3/reference/en/html/example-parentchild.html
I’d emphasize that ‘delete-orphan’ handles the case when we want to delete the child object from the parent’s collection and have this reflected in the database without having to do an explicit session.delete() on the child objects. e.g.
1) Without delete-orphan:
stock.getStockDailyRecords().remove(sdr1);
stock.getStockDailyRecords().remove(sdr2);
sdr1.setStock(null);
sdr2.setStock(null);
session.saveOrUpdate(stock);
results in an update to the the StockDailyRecord table to set the Foreign Key to Stock for sdr1 and sdr2 to null (keeps those records in that table)
2) With delete-orphan:
The same code as above results in sdr1 and sdr2 being deleted.
[...] Saving the session.save(stockDailyRecords); is not necessary, you can enable the cascade option to save the stockDailyRecords automatically, read more about it… [...]
why delete-orphan doesn’t work?
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
Book book = (Book)session.get(Book.class, 1);
tx.commit();
session.close();
Session session2 = HibernateUtil.getSession();
Transaction tx2 = session2.beginTransaction();
Publisher publisher = (Publisher)session2.get(Publisher.class, 1);
publisher.getBooks().remove(book);
session.saveOrUpdate(publisher);
tx2.commit();
session2.close();
Not work cause you close session 1 too soon
Excellent Work! best explanation of inverse i could find on the internet.
Your examples arequite simple and easy to understand..thanks and make I wish to have some more examples in spring mvc.
Please visit this collection of Spring MVC tutorials.
You simply doing great job man. With such simple example you making people knowledgeable about complex concpets of hibernate.
SIMPLY GREAT!!!
Thanks man! It’s help so much!
good article buddy. keep it up.. thx.
[...] Cascade example (save, update, delete and delete-orphan) Cascade examples in save, update, delete and delete orphan. And the different between delete and delete orphan. [...]