Cascade – JPA & Hibernate annotation common mistake
Often times, developers are mix used the JPA and Hibernate annotation together, it will caused a very common mistake – JPA cascade type annotation is not working in Hibernate?
During the code review section, i find out many Java developers are not aware of this mistake and causing the program failed to execute the cascade operation to the related entities. I will take this one-to-many hibernate example for the demonstration.
Mistake
In the one-to-many example, many developers declared the JPA cascade options as following :
... @Entity @Table(name = "stock", catalog = "mkyong", uniqueConstraints = { @UniqueConstraint(columnNames = "STOCK_NAME"), @UniqueConstraint(columnNames = "STOCK_CODE") }) public class Stock implements java.io.Serializable { ... private Set<StockDailyRecord> stockDailyRecords = new HashSet<StockDailyRecord>(0); @OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST,CascadeType.MERGE }, mappedBy = "stock") public Set<StockDailyRecord> getStockDailyRecords() { return this.stockDailyRecords; } public void setStockDailyRecords(Set<StockDailyRecord> stockDailyRecords) { this.stockDailyRecords = stockDailyRecords; } ...
Save it with Hibernate session.
stockDailyRecords.setStock(stock); stock.getStockDailyRecords().add(stockDailyRecords); session.save(stock); session.getTransaction().commit();
What the code trying to do is when you save a “stock”, it will save the associated stockDailyRecords as well. Everything look fine, but THIS IS NOT WORKING, the cascade options will not execute and save the stockDailyRecords. Can you spot the mistake?
Explanation
Look in the code, @OneToMany is from JPA , it expected a JPA cascade – javax.persistence.CascadeType. However when you save it with Hibernate session, org.hibernate.engine.Cascade will do the following checking …
if ( style.doCascade( action ) ) { cascadeProperty( persister.getPropertyValue( parent, i, entityMode ), types[i], style, anything, false ); }
The Hibernate save process will causing a ACTION_SAVE_UPDATE action, but the JPA will pass a ACTION_PERSIST and ACTION_MERGE, it will not match and causing the cascade failed to execute.
@see source code
- org.hibernate.engine.Cascade
- org.hibernate.engine.CascadeStyle
- org.hibernate.engine.CascadingAction
Solution
Delete the JPA cascade – javax.persistence.CascadeType, replace it with Hibernate cascade – org.hibernate.annotations.Cascade, with CascadeType.SAVE_UPDATE.
... @Entity @Table(name = "stock", catalog = "mkyong", uniqueConstraints = { @UniqueConstraint(columnNames = "STOCK_NAME"), @UniqueConstraint(columnNames = "STOCK_CODE") }) public class Stock implements java.io.Serializable { ... private Set<StockDailyRecord> stockDailyRecords = new HashSet<StockDailyRecord>(0); @OneToMany(fetch = FetchType.LAZY, mappedBy = "stock") @Cascade({CascadeType.SAVE_UPDATE}) public Set<StockDailyRecord> getStockDailyRecords() { return this.stockDailyRecords; } public void setStockDailyRecords(Set<StockDailyRecord> stockDailyRecords) { this.stockDailyRecords = stockDailyRecords; } ...
Now, it work as what you expected.
Conclusion
It look like an incompatible issue between JPA and Hibernate cascade annotation, if Hibernate is a JPA implementation, what may causing the misunderstand in between?
I am using JPA2 and hibernate 3.6. Until now I was able to implement everything I wanted using only JPA-annotations.
I exclusively use JPA-Annotations and EntityManager. And there’s absolutely nothing wrong (except for and probably atomikos related issue, which is likely to be a configuration fault).
I think the main mistake is mixing up hibernate and JPA. At design time you should make a decision which one you are going to use and then you have to stick to it. If you don’t you’re using bad design.
In my opinion it would be less confusion for beginners, if you point this out. This topic suggests Hibernate has a generel problem with CascadeType or maybe other JPA-annotations.
[...] options that are not compatible with Hibernate’s CascadeType (see this link for more info http://www.mkyong.com/hibernate/cascade-jpa-hibernate-annotation-common-mistake/).I was hoping to get a bit more clarification on the problem. I have some particular questions:1) [...]
I cannot thank you enough! You saved me from spending many more hours digging around and “experimenting” to solve foreign key constraint issues. :’) I have a comment though: for the Hibernate CascadeType to “kick in” I still needed to specify the JPA one in my entity. Per Hibernate’s documentation on cascading ( http://docs.jboss.org/hibernate/stable/annotations/reference/en/html/entity.html#entity-hibspec-cascade ) this is recommended, but in my case it seems to be actually required
Anyway, thanks a lot, again
You’re welcome, thanks for sharing your experience as well.
Thanks for this post. I had to debug all the way down to figure this insconsistency.
Yep ran into this issue headfirst…and helped me solve. Thanks a bunch.
But now my code is tied to Hibernate – which goes against JPA principles:)
So is there a newer version of JPA that fixes this issue?
I couldn’t find this problem mentioned anywhere else.
Does Oracle/Sun know about this issue to fix in their next version?
would you know?
I just found this conflict few months ago, may be you can ask this question in Hibernate forum
[...] Cascade – JPA & Hibernate annotation common mistake A super easy common annotation mistake made by beginner or experienced Hibernate developers – JPA cascade annotation in Hibernate? [...]
I thought you should be using EntityManager which is created to solve problems of such nature.
yes for this info, but some projects may not want to couple the JPA engine.
CascadeType.PERSIST will work when using session.persist() instead of session.save()