Sunday, February 13, 2011

Hibernate inverse="true" - Understanding The Logic Engine


What is “inverse”?

This is the most confusing keyword in Hibernate, at least i took quite a long time to understand what is it. The “inverse” keyword is always declared in one-to-many and many-to-many relationship (many-to-one doesn’t has inverse keyword), It defines which side is responsible to take care of the relationship.

Always put inverse=”true” in your collection variable?

There are many Hibernate articles try to explain the “inverse” with many Hibernate “official” jargon, which is very hard to understand (at least to me). In few articles, they even suggested that just forget about what is “inverse”, and always put the inverse=”true” in the collection variable. This statement is always true – “put inverse=true in collection variable”, but do not blindfold on it, try to understand the reason behind is essential to optimal your Hibernate performance.

“inverse”, can it change to “relationship owner”?

In Hibernate, only the relationship owner should maintain the relationship, and the “inverse” keyword is created to defines which side is the owner to maintain the relationship. However the “inverse” keyword itself is not verbose enough, I would suggest change the keyword to “relationship_owner“. The inverse=”true” means this is the relationship owner, whereas inverse=”false” (default) means it’s not.
Let’s go though a quick example to grab a draft idea of “inverse”…

Preparing the data for demonstration…

1. Database tables

This is a one-to-many relationship table design, a STOCK table has many occurrences in STOCK_DAILY_RECORD table.


2. Hibernate implementation

Stock.java
public class Stock implements java.io.Serializable {
   ...
   private Set<StockDailyRecord> stockDailyRecords = 
                                  new HashSet<StockDailyRecord>(0);
   ...
StockDailyRecord.java
public class StockDailyRecord implements java.io.Serializable {
   ...
   private Stock stock;
   ...
Stock.hbm.xml
>
     name="com.mkyong.common.Stock" table="stock" ...>
    ...
     name="stockDailyRecords" table="stock_daily_record" fetch="select">
        >
             name="STOCK_ID" not-null="true" />
        >
         class="com.mkyong.common.StockDailyRecord" />
    >
    ...
StockDailyRecord.hbm.xml
>
   name="com.mkyong.common.StockDailyRecord" table="stock_daily_record" ...>
  ...
   name="stock" class="com.mkyong.common.Stock">
        name="STOCK_ID" not-null="true" />
  >
  ...

3. Question …

See this file – “Stock.hbm.xml“.
>
     name="com.mkyong.common.Stock" table="stock" ...>
    ...
     name="stockDailyRecords" table="stock_daily_record" fetch="select">
        >
             name="STOCK_ID" not-null="true" />
        >
         class="com.mkyong.common.StockDailyRecord" />
    >
    ...
If the Set variable (stockDailyRecords) in Stock object is modified, and save the Stock object like following
Stock stock = new Stock();
stock.getStockDailyRecords().add(stockDailyRecords);
session.save(stock);
Will it update the StockDailyRecord table?
Hibernate: 
    update mkyong.stock_daily_record 
    set STOCK_ID=? 
    where DAILY_RECORD_ID=?

4. Answer …

inverse” is controlling the above scenario, to define which side should maintain the relationship.
  • Inverse = false (default) – will execute “update mkyong.stock_daily_record” and update the relationship.
  • Inverse = true – Do nothing.
Got it? May be not, let’s explore more examples to understand about it.

“Inverse = false” example

If no “inverse” keyword is declared, the default is inverse = “false”, which is equal to

...
 name="stockDailyRecords" inverse="false" table="stock_daily_record" ...>
It means both sides are the owner of the relationship. In Hibernate, it will ask both sides to update the foreign key “stock_daily_record.STOCK_ID” in “StockDailyRecord” table.
  • Stock will update the foreign key “stock_daily_record.STOCK_ID” if Set variable (stockDailyRecords) is modified.
  • StockDailyRecord will update the foreign key “stock_daily_record.STOCK_ID” with Stock property as well.

1. Insert example …

Here’s an insert example for inverse=”false”, when a Stock object is saved, Hibernate will generated two SQLSta, one insert and one update.
Stock stock = new Stock();
stock.getStockDailyRecords().add(stockDailyRecords);
session.save(stock);
Output
Hibernate: 
    insert into mkyong.stock (STOCK_CODE, STOCK_NAME) 
    values (?, ?)
...
Hibernate: 
    update mkyong.stock_daily_record 
    set STOCK_ID=? 
    where DAILY_RECORD_ID=?
Stock will update the “stock_daily_record.STOCK_ID” through Set variable (stockDailyRecords), because Stock is the relationship owner.

2. Update example …

Here’s an update example for inverse=”false”, Hibernate will generated three SQL statements, one insert and two updates.
        Query q = session.createQuery("from Stock where stockCode = :stockCode ");
        q.setParameter("stockCode", "4715");
        Stock stock = (Stock)q.list().get(0);
        stock.setStockName("GENM1");
 
        StockDailyRecord stockDailyRecords = new StockDailyRecord();
        //set stockDailyRecords data
 
        stockDailyRecords.setStock(stock);        
        stock.getStockDailyRecords().add(stockDailyRecords);
 
        session.save(stockDailyRecords);
        session.update(stock);
Output
Hibernate: 
    insert into mkyong.stock_daily_record (STOCK_ID, ...) 
    values (?, ...)
 
Hibernate: 
    update mkyong.stock 
    set STOCK_CODE=?, STOCK_NAME=? 
    where STOCK_ID=?
 
Hibernate: 
    update mkyong.stock_daily_record 
    set STOCK_ID=? 
    where DAILY_RECORD_ID=?
Stock will update the “stock_daily_record.STOCK_ID” through Set variable (stockDailyRecords), because Stock is the relationship owner.
Note
Wait…do you think the third update statement is necessary? The inverse = “true” in Set variable (stockDailyRecords) can stop the Hibernate to generate the unnecessary third update statement.

inverse = “true” example

The inverse=”true” is declared at the Set variable (stockDailyRecords), which means Stock is not the relationship owner, the owner is belong to StockDailyRecord class. In Hibernate, it will enable only the StockDailyRecord class to update the foreign key “stock_daily_record.STOCK_ID” in StockDailyRecord table.

 name="stockDailyRecords" inverse="true" table="stock_daily_record" ...>
  • Stock will not update the foreign key “stock_daily_record.STOCK_ID” if Set variable (stockDailyRecords) is modified.
  • Only StockDailyRecord will update the foreign key “stock_daily_record.STOCK_ID” with Stock property.

1. Insert example …

Here’s an insert example for inverse=”true”, when a Stock object is saved, Hibernate will generated one insert SQL statement.
Stock stock = new Stock();
stock.getStockDailyRecords().add(stockDailyRecords);
session.save(stock);
Output
Hibernate: 
    insert into mkyong.stock (STOCK_CODE, STOCK_NAME) 
    values (?, ?)
Since “Stock” is not the owner of the relationship, it will not update the “stock_daily_record.STOCK_ID” in StockDailyRecord table.

2. Update example …

Here’s an update example for inverse=”true”, Hibernate will generated two SQL statements, one insert and one update.
        Query q = session.createQuery("from Stock where stockCode = :stockCode ");
        q.setParameter("stockCode", "4715");
        Stock stock = (Stock)q.list().get(0);
        stock.setStockName("GENM1");
 
        StockDailyRecord stockDailyRecords = new StockDailyRecord();
        //set stockDailyRecords data
 
        stockDailyRecords.setStock(stock);        
        stock.getStockDailyRecords().add(stockDailyRecords);
 
        session.save(stockDailyRecords);
        session.update(stock);
Output
Hibernate: 
    insert into mkyong.stock_daily_record (STOCK_ID, ...) 
    values (?, ...)
 
Hibernate: 
    update mkyong.stock 
    set STOCK_CODE=?, STOCK_NAME=? 
    where STOCK_ID=?
Since “Stock” is not the owner of the relationship, it will not update the “stock_daily_record.STOCK_ID” in stockDailyRecord table.
inverse vs cascade
Many people like to compare between inverse and cascade, but both are totally different notions, see the differencial here.

Conclusion

Understanding the “inverse” is essential to optimize your Hibernate code, it helps to avoid many unnecessary update statements, which mention in the “update example for inverse=false” above. At last, try to remember the inverse=”true” mean this is the relationship owner to handle the relationship.

Reference

  1. http://www.mkyong.com/hibernate/inverse-true-example-and-explanation/
  2. http://simoes.org/docs/hibernate-2.1/155.html
  3. http://docs.jboss.org/hibernate/stable/core/reference/en/html/example-parentchild.html
  4. http://tadtech.blogspot.com/2007/02/hibernate-when-is-inversetrue-and-when.html






No comments: