Hibernate中inverse屬性與cascade屬性
Hibernate集合映射中,經(jīng)常會使用到"inverse"和"cascade"這兩個屬性。對于我這樣,Hibernate接觸不深和語文水平夠爛的種種因素,發(fā)現(xiàn)這兩個屬性實(shí)在是難以理解,無奈只好將這個兩個屬性解釋工作交給了Google和Baidu,查看了許多牛人的解釋,加上自己在Eclipse上的調(diào)試,對"inverse"和"cascade"這兩個屬性有了一定的見解。
"inverse"屬性探究
"inverse"-直譯過來就是"反轉(zhuǎn),使顛倒"的意思,書面化的解釋為"是否將關(guān)系維護(hù)的權(quán)力交給對方"(這個解釋真夠蛋疼的-_-!!,就是理解不了)。 Hibernate中的"inverse"屬性只有兩個值"true"和"false"。"true"表示將關(guān)系維護(hù)的權(quán)力交給對方,"false"表示不交出維護(hù)權(quán)力(默認(rèn)值)。
例如有兩張表,customer和orders,他們的關(guān)系是一對多,customer是一方,orders為多方。
- drop table if exists customer;
- drop table if exists orders;
- create table customer
- (
- id varchar(255) not null,
- username varchar(255),
- password varchar(255),
- age integer,
- register_time datetime,
- primary key (id)
- );
- create table orders
- (
- id varchar(255) not null,
- orderNumber varchar(255),
- balance integer,
- customer_id varchar(255),
- primary key (id)
- );
兩表對應(yīng)的hbm文件,對應(yīng)的POJO類:
- /*customer表對應(yīng)的POJO類*/
- public class Customer
- {
- private String id;
- private String username;
- private String password;
- private Timestamp registerTime;
- private int age;
- private Set<Order> orders = new HashSet<Order>();
- public Customer()
- {
- }
- /*get and set method*/
- }
- /*orders表對應(yīng)的POJO類*/
- public class Order
- {
- private String id;
- private String orderNumber;
- private int balance;
- private Customer customer;
- public Order()
- {
- }
- /* get and set method*/
- }
- <!--Customer類的hbm文件-->
- <hibernate-mapping>
- <class name="com.suxiaolei.hibernate.pojos.Customer" table="customer">
- <id name="id" type="string">
- <column name="id"></column>
- <generator class="uuid"></generator>
- </id>
- <property name="username" column="username" type="string"></property>
- <property name="password" column="password" type="string"></property>
- <property name="age" column="age" type="integer"></property>
- <property name="registerTime" column="register_time" type="timestamp"></property>
- <set name="orders" inverse="true" cascade="all">
- <key column="customer_id" ></key>
- <one-to-many class="com.suxiaolei.hibernate.pojos.Order"/>
- </set>
- </class>
- </hibernate-mapping>
- <!--Order類的hbm文件-->
- <hibernate-mapping>
- <class name="com.suxiaolei.hibernate.pojos.Order" table="orders">
- <id name="id" type="string">
- <column name="id"></column>
- <generator class="uuid"></generator>
- </id>
- <property name="orderNumber" column="orderNumber" type="string"></property>
- <property name="balance" column="balance" type="integer"></property>
- <many-to-one name="customer" class="com.suxiaolei.hibernate.pojos.Customer">
- <column name="customer_id"></column>
- </many-to-one>
- </class>
- </hibernate-mapping>
下面寫一些測試代碼測試"inverse"屬性的特性:
情況一:將"inverse"設(shè)置為true,讓多方維護(hù)關(guān)系
- try
- {
- tx = session.beginTransaction();
- /*
- * 創(chuàng)建Customer對象,并設(shè)置其屬性值
- */
- Customer customer = new Customer();
- customer.setUsername("zhangsan");
- customer.setPassword("123456");
- customer.setAge(22);
- customer.setRegisterTime(new Timestamp(new Date().getTime()));
- /*
- * 創(chuàng)建Order對象order1,并設(shè)置其屬性值
- */
- Order order1 = new Order();
- order1.setOrderNumber("a1a2a3");
- order1.setBalance(1000);
- order1.setCustomer(customer);//將customer對象關(guān)聯(lián)到order1對象上
- /*
- * 創(chuàng)建Order對象order2,并設(shè)置其屬性值
- */
- Order order2 = new Order();
- order2.setOrderNumber("d3d2d1");
- order2.setBalance(670);
- order2.setCustomer(customer);///將customer對象關(guān)聯(lián)到order2對象上
- customer.getOrders().add(order1);//將order1對象關(guān)聯(lián)到customer對象上
- customer.getOrders().add(order2);//將order2對象關(guān)聯(lián)到customer對象上
- session.saveOrUpdate(customer);
- tx.commit();
- }
- catch (Exception e)
- {
- if(tx != null)
- {
- tx.rollback();
- }
- e.printStackTrace();
- }
- finally
- {
- session.close();
- }
數(shù)據(jù)庫中的數(shù)據(jù)更新為:
customer表:
orders表:
現(xiàn)在將order1.setCustomer(customer);這段代碼注釋掉,再次運(yùn)行程序:
customer表:
orders表:
可以到看到顯著地差別了,第一次保存"id"="402881e534ea7c750134ea7c76bc0001"的數(shù)據(jù)時,orders表中插入了兩條數(shù)據(jù),他們的customer_id都為customer中對應(yīng)記錄的主鍵值,而第二次保存記錄"id"="402881e534ea81be0134ea81bfea0001"的數(shù)據(jù)時,由于先前將原來的代碼段order1.setCustomer(customer);注釋掉了,此時order表中插入的數(shù)據(jù)中order1代表的那條記錄沒有customer_id值。
從以上現(xiàn)象可以有助于理解"inverse"這個屬性。首先,"inverse"控制關(guān)系維護(hù)權(quán)力,那么什么是"關(guān)系"?,關(guān)系的具體體現(xiàn)是什么?在以上例子中,"關(guān)系"就是兩個表之間的關(guān)系,通常為"一對多","一對一","多對多"三種關(guān)系,而關(guān)系的具體體現(xiàn)為orders表中的 customer_id列,而"inverse"屬性就是告訴Hibernate哪一方有權(quán)力管理和維護(hù)這一列。上面的例子將"inverse"設(shè)置為 true那么customer_id這一列由多方(order對象)維護(hù)。這說明了,只有order對象對關(guān)系的操作會反映到數(shù)據(jù)庫中。(對象對關(guān)系的操作就是對關(guān)聯(lián)屬性的操作,例如order對象對自身的"customer"屬性操作,customer對象對自身的orders集合(Set<Order>)操作)
例如,將id="402881e534ea7c750134ea7c76bc0001"的customer對象從數(shù)據(jù)庫中取出,獲取到該customer對象所關(guān)聯(lián)的order對象集合,將該customer對象所關(guān)聯(lián)的order對象刪除。
- Customer customer = (Customer)session.get(Customer.class, "402881e534ea7c750134ea7c76bc0001");
- Order order = (Order)session.get(Order.class, "402881e534ea7c750134ea7c76ce0002");
- System.out.println("customer association order count:"+customer.getOrders().size());
- customer.getOrders().remove(order);
- System.out.println("customer association order count:"+customer.getOrders().size());
- session.saveOrUpdate(customer);
//Console Output:
customer association order count:2
customer association order count:1
可以看到customer中關(guān)聯(lián)的order對象集合確實(shí)有對象被刪除了,若操作有效,表示該order對象與customer對象沒有關(guān)系了,反映到數(shù)據(jù)庫中應(yīng)該將該order對象對應(yīng)的customer_id設(shè)置為null?,F(xiàn)在查看一下數(shù)據(jù)庫數(shù)據(jù):
看到了吧,剛剛那個操作就是個無用操作,不會反應(yīng)到數(shù)據(jù)庫中。我們修改一下程序代碼:
- Customer customer = (Customer)session.get(Customer.class, "402881e534ea7c750134ea7c76bc0001");
- Order order = (Order)session.get(Order.class, "402881e534ea7c750134ea7c76ce0002");
- order.setCustomer(null);
- session.saveOrUpdate(customer);
這次我們使用order對象來操作關(guān)系,將該order對象與customer對象脫離關(guān)系,若操作有效,則反映在數(shù)據(jù)庫中應(yīng)該是該order對象的customer_id字段的值變成null,現(xiàn)在查看一下數(shù)據(jù)庫:
可以看到,此次操作成功的反映到了數(shù)據(jù)庫中了。
情況二:將"inverse"屬性設(shè)置為"false",雙方都維護(hù)關(guān)系(因?yàn)闆]有一方交出權(quán)力,"inverse"的默認(rèn)值為"false",而且"inverse"屬性只能在set、list、map等幾個標(biāo)簽中設(shè)置,像many-to-one這一類的標(biāo)簽都不能設(shè)置"inverse"這個屬性值,它們只能取值"false")
這里會產(chǎn)生書中所說的性能問題(囧,這個也是理解了很久很久),這個不管怎么說你都可能理解不了,我就是這樣的(-_-!!),所以我建議使用第三方的軟件將Hibernate輸出的SQL語句的綁定值顯示出來(可以參考這里)。之所以會產(chǎn)生性能為題,當(dāng)你操作關(guān)系是會無故多產(chǎn)生一些update語句,比如你使用上面的例子保存一個customer對象,它關(guān)聯(lián)了2個order對象,它不但會生成3條insert語句(用于插入數(shù)據(jù)),還會生成2條update語句(將關(guān)聯(lián)的order對象的customer_id更新為自己的主鍵值),你想想要是一個customer對象包含幾萬了order對象(購物狂),那么每次保存它得要多生成幾萬條update語句,這個就是很嚴(yán)重的性能問題了。
為什么Hibernate會產(chǎn)生update語句呢?那是Hibernate太主動,太熱情,太負(fù)責(zé)的表現(xiàn),它怕你出現(xiàn)錯誤,例如有幾萬個order對象需要關(guān)聯(lián)到customer對象上,這就需要調(diào)用order.setCustomer(customer);,幾萬個對象這不是人可以不放錯的完成的。所以Hibernate怕你出錯忘記調(diào)用這個方法,所以他將會在order對象保存完畢后將所有關(guān)聯(lián)對象的customer_id字段更新一遍,確保正確性,這樣也就產(chǎn)生上面的性能問題。
將"inverse"設(shè)置為false后,你可以嘗試設(shè)置order1.setCustomer(null),它依然會正確的將customer的主鍵值完美的插入到order的customer_id字段上,只是會多一條update語句。
"cascade"屬性
"cascade"-直譯過來就是"級聯(lián)、串聯(lián)"的意思,書面化的解釋為"該屬性會使我們在操作主對象時,同時Hibernate幫助我們完成從屬對象相應(yīng)的操作(比如,有Customer和Order這兩張表,關(guān)系為一對多,只使用JDBC刪除Customer表中的一行記錄時,我們還需要手動的將Order表中與之關(guān)聯(lián)的記錄全都刪除,使用Hibernate的'cascade'屬性后,當(dāng)我們刪除一條Customer記錄時,Hibernate會幫助我們完成相應(yīng)Order表記錄的刪除工作,方便了我們的工作)"。
總 結(jié)
使用"inverse"這個屬性時,要考慮清楚關(guān)系,不然你的系統(tǒng)就會有大的性能問題(不過我可能想不清楚,現(xiàn)在還是一個普通大學(xué)生沒什么實(shí)戰(zhàn)經(jīng)驗(yàn)-_-!!,要繼續(xù)努力~_~),書本上和一些牛人建議,關(guān)系一般由"多方"維護(hù),當(dāng)遇到"多對多"時怎么辦,其實(shí)多對多久是兩個"一對多",隨意設(shè)置一方"inverse"為"true"就可以了,不要兩方都設(shè)置或都不設(shè)置(囧,我開始就是死板這樣的設(shè)置)。而是用"cascade"屬性時,主對象(一方)一般設(shè)置為"all",而多方不建議設(shè)置包含delete操作的選項(xiàng),建議設(shè)置多方為"save-update",這是因?yàn)槟銊h除一方,多方已經(jīng)沒有存在的意義了,而刪除多方不能代表一方?jīng)]意義了(例如,消費(fèi)者和訂單)。最后,"cascade"操作的是兩張表的記錄或兩端的對象,而"inverse"操作的是兩張表的關(guān)系或兩個對象的關(guān)系。
原文鏈接:http://www.cnblogs.com/otomedaybreak/archive/2012/01/17/2324772.html
【編輯推薦】