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 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.
What is “inverse” ?
This is the most confusing keyword in Hibernate, at least i took quite a long time to understand it. The “inverse” keyword is always declare in one-to-many and many-to-many relationship (many-to-one doesn’t has inverse keyword), it means which side is responsible to take care of the relationship.
“inverse”, should 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“.
In short, inverse=”true” means this is the relationship owner, and inverse=”false” (default) means it’s not.
Customer类:
public class Customer { private int id; private String name; private Set orders = new HashSet(); //getter / setter }
即Customer类具有一个set集合属性orders,其中Order是一个普通的类:
public class Order { private int id; private String orderName; //getter / setter }
数据库中表的结构:
t_customer: 两个字段:id name t_order: 三个字段:id orderName customerid
Customer类的映射文件:Customer.hbm.xml (Order类的映射文件忽略)
<hibernate-mapping> <class name="test.Customer" table="t_customer" lazy="false"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <set name="orders" cascade="save-update" lazy="false"> <key column="customerid"/> <one-to-many class="test.Order"/> </set> </class> </hibernate-mapping>
执行如下代码:
Set orders = new HashSet(); Order o1 = new Order(); o1.setOrderName("o1"); Order o2 = new Order(); o2.setOrderName("o2"); orders.add(o1); orders.add(o2); Customer c = new Customer(); c.setName("aaa"); c.setOrders(orders); session.save(c);
此时Hibernate发出的sql语句如下:
Hibernate: insert into t_customer (name) values (?) Hibernate: insert into t_order (orderName) values (?) Hibernate: insert into t_order (orderName) values (?) Hibernate: update t_order set customerid=? where id=? Hibernate: update t_order set customerid=? where id=?
查看数据库:
t_customer : t_order: id | name id | orderName | customerid 1 aaa 1 o1 1 2 o2 1
保存Customer对象时,首先发出insert into t_customer (name) values (?)语句将c同步到数据库,由于在<set>映射中设置cascade="save-update",所以会同时保存orders集合中的 Order类型的o1,o2对象(如果没有这个设置,即cascade="save-update"),那么Hibenrate不会自动保存orders 集合中的对象,那么在更新时将会抛出如下异常:
Hibernate: insert into t_customer (name) values (?) Hibernate: update t_order set customerid=? where id=? org.hibernate.TransientObjectException: test.Order ••••••
抛出这一异常的原因是:<set>映射默认"inverse=fasle"即由Customer对象作为主控方,那么它要负责关联的 维护工作,在这里也就是负责更新t_order表中的customerid字段的值,但由于未设置cascade="save-update",所以 orders集合中的对象不会在保存customer时自动保存,因此会抛出异常(如果未设置,需要手动保存)。
现在设置cascade="save-update",同时设置inverse="true",即:
••• <set name="orders" cascade="save-update" inverse="true" lazy="false"> <key column="customerid"/> <one-to-many class="test.Order"/> </set> •••
同样执行上述代码,发出如下语句:
Hibernate: insert into t_customer (name) values (?) Hibernate: insert into t_order (orderName) values (?) Hibernate: insert into t_order (orderName) values (?)
相比上一次执行,少了两条update语句,查看数据库:
t_customer : t_order: id | name id | orderName | customerid 1 aaa 1 o1 NULL 2 o2 NULL
发现t_order表中customerid的值为NULL,这是由于设置了inverse="true",它意味着
Customer不再作为主控方,而将关联关系的维护工作交给关联对象Orders来完成。在保存Customer时,Customer不在关心 Orders的customerid属性,必须由Order自己去维护,即设置order.setCustomer(customer);
如果需要通过Order来维护关联关系,那么这个关联关系转换成双向关联。
修改Order类代码:
public class Order { private int id; private String orderName; private Customer customer; ••• }
Order.hbm.xml:
<hibernate-mapping> <class name="test.Order" table="t_order"> <id name="id"> <generator class="native"/> </id> <property name="orderName"/> <many-to-one name="customer" column="customerid"/> </class> </hibernate-mapping>
此时数据库中表的结构不会变化。
再次执行上述代码,发出如下sql语句:
Hibernate: insert into t_customer (name) values (?) Hibernate: insert into t_order (orderName, customerid) values (?, ?) Hibernate: insert into t_order (orderName, customerid) values (?, ?)
发现在保存Order对象时为customerid字段赋值,因为Order对象中拥有Customer属性,对应customerid字段,查看数据库表:
t_customer : t_order: id | name id | orderName | customerid 1 aaa 1 o1 NULL 2 o2 NULL
发现customerid的值仍为NULL,因为在上述代码中并未设置Order对象的Customer属性值,由于设置了inverse="true",所以Order对象需要维护关联关系,所以必须进行设置,即
order.setCustomer(customer);
修改上述代码为:
••• Customer c = new Customer(); Set orders = new HashSet(); Order o1 = new Order(); o1.setOrderName("o1"); o1.setCustomer(c); Order o2 = new Order(); o2.setOrderName("o2"); o2.setCustomer(c); orders.add(o1); orders.add(o2); c.setName("aaa"); c.setOrders(orders); session.save(c); •••
执行上述代码,发出如下语句:
Hibernate: insert into t_customer (name) values (?) Hibernate: insert into t_order (orderName, customerid) values (?, ?) Hibernate: insert into t_order (orderName, customerid) values (?, ?)
查看数据库:
t_customer : t_order: id | name id | orderName | customerid 1 aaa 1 o1 1 2 o2 1
发现已经设置了customerid的值。
在一对多关联中,在一的一方设置inverse="true",有助于性能的改善。通过上述分析可以发现少了update语句。