Hibernate中inverse属性与cascade属性

开发 后端
Hibernate集合映射中,经常会使用到"inverse"和"cascade"这两个属性。对于我这样,Hibernate接触不深和语文水平够烂的种种因素,发现这两个属性实在是难以理解,无奈只好将这个两个属性解释工作交给了Google和Baidu,查看了许多牛人的解释,加上自己在Eclipse上的调试,对"inverse"和"cascade"这两个属性有了一定的见解。

Hibernate集合映射中,经常会使用到"inverse"和"cascade"这两个属性。对于我这样,Hibernate接触不深和语文水平够烂的种种因素,发现这两个属性实在是难以理解,无奈只好将这个两个属性解释工作交给了Google和Baidu,查看了许多牛人的解释,加上自己在Eclipse上的调试,对"inverse"和"cascade"这两个属性有了一定的见解。

"inverse"属性探究

"inverse"-直译过来就是"反转,使颠倒"的意思,书面化的解释为"是否将关系维护的权力交给对方"(这个解释真够蛋疼的-_-!!,就是理解不了)。 Hibernate中的"inverse"属性只有两个值"true"和"false"。"true"表示将关系维护的权力交给对方,"false"表示不交出维护权力(默认值)。

例如有两张表,customer和orders,他们的关系是一对多,customer是一方,orders为多方。

  1. drop table if exists customer;  
  2. drop table if exists orders;  
  3.  
  4. create table customer  
  5. (  
  6.     id varchar(255) not null,  
  7.     username varchar(255),  
  8.     password varchar(255),  
  9.     age integer,  
  10.     register_time datetime,  
  11.     primary key (id)  
  12. );  
  13.  
  14. create table orders  
  15. (  
  16.     id varchar(255) not null,  
  17.     orderNumber varchar(255),  
  18.     balance integer,  
  19.     customer_id varchar(255),  
  20.     primary key (id)  
  21. ); 

两表对应的hbm文件,对应的POJO类:

  1. /*customer表对应的POJO类*/ 
  2. public class Customer  
  3. {  
  4.     private String id;  
  5.     private String username;  
  6.     private String password;  
  7.     private Timestamp registerTime;  
  8.     private int age;  
  9.     private Set<Order> orders = new HashSet<Order>();  
  10.  
  11.     public Customer()  
  12.     {  
  13.  
  14.     }  
  15.  
  16.     /*get and set method*/ 
  17.  
  18. }  
  19.  
  20. /*orders表对应的POJO类*/ 
  21. public class Order  
  22. {  
  23.     private String id;  
  24.     private String orderNumber;  
  25.     private int balance;  
  26.     private Customer customer;  
  27.       
  28.     public Order()  
  29.     {  
  30.           
  31.     }  
  32.  
  33.     /* get and set method*/ 

 

  1. <!--Customer类的hbm文件-->  
  2.     <hibernate-mapping>  
  3.         <class name="com.suxiaolei.hibernate.pojos.Customer" table="customer">  
  4.             <id name="id" type="string">  
  5.                 <column name="id"></column>  
  6.                 <generator class="uuid"></generator>  
  7.             </id>  
  8.               
  9.             <property name="username" column="username" type="string"></property>  
  10.             <property name="password" column="password" type="string"></property>  
  11.             <property name="age" column="age" type="integer"></property>  
  12.             <property name="registerTime" column="register_time" type="timestamp"></property>  
  13.               
  14.             <set name="orders" inverse="true" cascade="all">  
  15.                 <key column="customer_id" ></key>  
  16.                 <one-to-many class="com.suxiaolei.hibernate.pojos.Order"/>  
  17.             </set>  
  18.           
  19.         </class>  
  20.     </hibernate-mapping>  
  21.  
  22. <!--Order类的hbm文件-->  
  23.     <hibernate-mapping>  
  24.         <class name="com.suxiaolei.hibernate.pojos.Order" table="orders">  
  25.             <id name="id" type="string">  
  26.                 <column name="id"></column>  
  27.                 <generator class="uuid"></generator>  
  28.             </id>  
  29.               
  30.             <property name="orderNumber" column="orderNumber" type="string"></property>  
  31.             <property name="balance" column="balance" type="integer"></property>  
  32.               
  33.             <many-to-one name="customer" class="com.suxiaolei.hibernate.pojos.Customer">  
  34.                 <column name="customer_id"></column>  
  35.             </many-to-one>          
  36.         </class>  
  37.     </hibernate-mapping> 

下面写一些测试代码测试"inverse"属性的特性:

情况一:将"inverse"设置为true,让多方维护关系

  1. try 
  2. {  
  3.     tx = session.beginTransaction();  
  4.       
  5.     /*  
  6.      * 创建Customer对象,并设置其属性值  
  7.      */ 
  8.     Customer customer = new Customer();  
  9.     customer.setUsername("zhangsan");  
  10.     customer.setPassword("123456");  
  11.     customer.setAge(22);  
  12.     customer.setRegisterTime(new Timestamp(new Date().getTime()));  
  13.       
  14.     /*  
  15.      * 创建Order对象order1,并设置其属性值  
  16.      */ 
  17.     Order order1 = new Order();  
  18.     order1.setOrderNumber("a1a2a3");  
  19.     order1.setBalance(1000);  
  20.     order1.setCustomer(customer);//将customer对象关联到order1对象上  
  21.       
  22.     /*  
  23.      * 创建Order对象order2,并设置其属性值  
  24.      */ 
  25.     Order order2 = new Order();  
  26.     order2.setOrderNumber("d3d2d1");  
  27.     order2.setBalance(670);  
  28.     order2.setCustomer(customer);///将customer对象关联到order2对象上  
  29.       
  30.     customer.getOrders().add(order1);//将order1对象关联到customer对象上  
  31.     customer.getOrders().add(order2);//将order2对象关联到customer对象上  
  32.       
  33.     session.saveOrUpdate(customer);  
  34.       
  35.     tx.commit();  
  36. }  
  37. catch (Exception e)  
  38. {  
  39.     if(tx != null)  
  40.     {  
  41.         tx.rollback();  
  42.     }  
  43.       
  44.     e.printStackTrace();  
  45. }  
  46. finally 
  47. {  
  48.     session.close();  

数据库中的数据更新为:

customer表: 

orders表:

现在将order1.setCustomer(customer);这段代码注释掉,再次运行程序:

customer表:

orders表:

可以到看到显著地差别了,第一次保存"id"="402881e534ea7c750134ea7c76bc0001"的数据时,orders表中插入了两条数据,他们的customer_id都为customer中对应记录的主键值,而第二次保存记录"id"="402881e534ea81be0134ea81bfea0001"的数据时,由于先前将原来的代码段order1.setCustomer(customer);注释掉了,此时order表中插入的数据中order1代表的那条记录没有customer_id值。

从以上现象可以有助于理解"inverse"这个属性。首先,"inverse"控制关系维护权力,那么什么是"关系"?,关系的具体体现是什么?在以上例子中,"关系"就是两个表之间的关系,通常为"一对多","一对一","多对多"三种关系,而关系的具体体现为orders表中的 customer_id列,而"inverse"属性就是告诉Hibernate哪一方有权力管理和维护这一列。上面的例子将"inverse"设置为 true那么customer_id这一列由多方(order对象)维护。这说明了,只有order对象对关系的操作会反映到数据库中。(对象对关系的操作就是对关联属性的操作,例如order对象对自身的"customer"属性操作,customer对象对自身的orders集合(Set<Order>)操作)

例如,将id="402881e534ea7c750134ea7c76bc0001"的customer对象从数据库中取出,获取到该customer对象所关联的order对象集合,将该customer对象所关联的order对象删除。

  1. Customer customer = (Customer)session.get(Customer.class"402881e534ea7c750134ea7c76bc0001");  
  2. Order order = (Order)session.get(Order.class"402881e534ea7c750134ea7c76ce0002");  
  3.               
  4. System.out.println("customer association order count:"+customer.getOrders().size());  
  5. customer.getOrders().remove(order);  
  6. System.out.println("customer association order count:"+customer.getOrders().size());  
  7.               
  8. session.saveOrUpdate(customer); 

//Console Output:

customer association order count:2
customer association order count:1

 

可以看到customer中关联的order对象集合确实有对象被删除了,若操作有效,表示该order对象与customer对象没有关系了,反映到数据库中应该将该order对象对应的customer_id设置为null。现在查看一下数据库数据:

看到了吧,刚刚那个操作就是个无用操作,不会反应到数据库中。我们修改一下程序代码:

  1. Customer customer = (Customer)session.get(Customer.class"402881e534ea7c750134ea7c76bc0001");  
  2. Order order = (Order)session.get(Order.class"402881e534ea7c750134ea7c76ce0002");  
  3.               
  4. order.setCustomer(null);  
  5.               
  6. session.saveOrUpdate(customer); 

这次我们使用order对象来操作关系,将该order对象与customer对象脱离关系,若操作有效,则反映在数据库中应该是该order对象的customer_id字段的值变成null,现在查看一下数据库:

可以看到,此次操作成功的反映到了数据库中了。

情况二:将"inverse"属性设置为"false",双方都维护关系(因为没有一方交出权力,"inverse"的默认值为"false",而且"inverse"属性只能在set、list、map等几个标签中设置,像many-to-one这一类的标签都不能设置"inverse"这个属性值,它们只能取值"false")

这里会产生书中所说的性能问题(囧,这个也是理解了很久很久),这个不管怎么说你都可能理解不了,我就是这样的(-_-!!),所以我建议使用第三方的软件将Hibernate输出的SQL语句的绑定值显示出来(可以参考这里)。之所以会产生性能为题,当你操作关系是会无故多产生一些update语句,比如你使用上面的例子保存一个customer对象,它关联了2个order对象,它不但会生成3条insert语句(用于插入数据),还会生成2条update语句(将关联的order对象的customer_id更新为自己的主键值),你想想要是一个customer对象包含几万了order对象(购物狂),那么每次保存它得要多生成几万条update语句,这个就是很严重的性能问题了。

为什么Hibernate会产生update语句呢?那是Hibernate太主动,太热情,太负责的表现,它怕你出现错误,例如有几万个order对象需要关联到customer对象上,这就需要调用order.setCustomer(customer);,几万个对象这不是人可以不放错的完成的。所以Hibernate怕你出错忘记调用这个方法,所以他将会在order对象保存完毕后将所有关联对象的customer_id字段更新一遍,确保正确性,这样也就产生上面的性能问题。

将"inverse"设置为false后,你可以尝试设置order1.setCustomer(null),它依然会正确的将customer的主键值完美的插入到order的customer_id字段上,只是会多一条update语句。

"cascade"属性

"cascade"-直译过来就是"级联、串联"的意思,书面化的解释为"该属性会使我们在操作主对象时,同时Hibernate帮助我们完成从属对象相应的操作(比如,有Customer和Order这两张表,关系为一对多,只使用JDBC删除Customer表中的一行记录时,我们还需要手动的将Order表中与之关联的记录全都删除,使用Hibernate的'cascade'属性后,当我们删除一条Customer记录时,Hibernate会帮助我们完成相应Order表记录的删除工作,方便了我们的工作)"。

总 结

使用"inverse"这个属性时,要考虑清楚关系,不然你的系统就会有大的性能问题(不过我可能想不清楚,现在还是一个普通大学生没什么实战经验-_-!!,要继续努力~_~),书本上和一些牛人建议,关系一般由"多方"维护,当遇到"多对多"时怎么办,其实多对多久是两个"一对多",随意设置一方"inverse"为"true"就可以了,不要两方都设置或都不设置(囧,我开始就是死板这样的设置)。而是用"cascade"属性时,主对象(一方)一般设置为"all",而多方不建议设置包含delete操作的选项,建议设置多方为"save-update",这是因为你删除一方,多方已经没有存在的意义了,而删除多方不能代表一方没意义了(例如,消费者和订单)。最后,"cascade"操作的是两张表的记录或两端的对象,而"inverse"操作的是两张表的关系或两个对象的关系。

原文链接:http://www.cnblogs.com/otomedaybreak/archive/2012/01/17/2324772.html

【编辑推荐】

  1. Hibernate复合主键映射
  2. Hibernate继承映射
  3. Hibernate事务与并发问题处理
  4. 让Hibernate显示SQL语句的绑定参数值
  5. Hibernate延迟加载剖析与代理模式应用
责任编辑:林师授 来源: 音①晓的博客
相关推荐

2011-04-07 11:06:18

Hibernate

2009-09-22 09:40:03

cascade和invHibernate

2009-06-12 14:52:21

cascadeinverseHibernate

2009-09-23 08:56:18

Hibernate cHibernate i

2009-06-29 08:59:05

hbm的generat

2009-09-23 13:33:51

Hibernate属性

2009-09-29 17:00:08

Hibernate c

2009-09-25 13:39:40

Hibernate属性

2009-09-28 09:56:53

Hibernate属性

2009-07-02 09:34:05

hibernate的l

2009-06-18 10:29:24

Hibernate I

2009-06-12 15:05:03

cascadeHibernate

2009-06-29 08:58:06

Hibernate的g

2010-09-07 14:40:10

title属性Alt属性CSS

2023-11-20 14:41:34

Python属性

2023-12-01 10:20:04

Python类属性

2010-09-07 16:21:37

CSSDisplayVisibility

2023-09-21 23:29:59

2010-09-16 10:29:47

DisplayVisibilityCSS

2021-05-08 09:49:07

JavaScript延迟加载
点赞
收藏

51CTO技术栈公众号