日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

Hibernate_2_Hibernate中对象状态及转化_一级缓存_Session详解_HQL/SQL/Criteria_一对多关系_级联操作

發布時間:2024/7/5 数据库 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Hibernate_2_Hibernate中对象状态及转化_一级缓存_Session详解_HQL/SQL/Criteria_一对多关系_级联操作 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Hibernate中的對象狀態

在Hibernate中持久化對象具有三種狀態: 瞬時態, 持久態, 游離態.

瞬時態: 對象沒有與Hibernate產生關聯(transient,session中沒有緩存), 數據庫中也沒有對應記錄=> 對象無id, 沒有關聯 持久態: 對象與Hibernate產生關聯(persistent, session中有緩存), 數據庫中會存在記錄=> 對象存有id, 有關聯 游離態: 對象沒有與Hibernate產生關聯(detached,session中沒有緩存), 數據庫中有記錄=> 對象有id, 沒有關聯

持久化對象狀態之間的相互轉化

  • 瞬時(無id, 無關聯)=>持久(有id, 有關聯) save操作:
    修改id, 與session的關聯狀態
讓瞬時對象擁有id, 并且與Hibernate產生關聯 //u此時沒有id, 沒有與Hibernate產生關聯 User u = new User(); //瞬時態 u.setName("tom"); //瞬時態 u.setPassword("123"); //瞬時態//save操作, Hibernate根據id生成策略, 為u生成id //然后存入數據庫, 與Hibernate產生關聯, 變為持久態 session.save(u);//持久態

注: 只有Hibernate的主鍵生成策略變為assigned才能手動設置id, 否則報錯

  • **瞬時(無id, 無關聯)=>游離(有id, 無關聯) **
    修改id即可
//設置Hibernate主鍵生成策略為assigned User u = new User(); u.setId(1);
  • 持久(有id, 有關聯)=>瞬時(無id, 無關聯)
    修改id與關聯狀態即可
1.直接通過session的get查詢操作獲取持久化對象關閉session切斷關聯, 再通過setId(null)修改id就變為瞬時態 User u = (User) session.get(User.class, 1); //持久態 session.close(); u.setId(null); //瞬時態2.get查詢到的對象, 使用evict方法切斷user與session的聯系,再設置id為null變為瞬時態 User u = session.get(User.class ,1);//持久態 session.evict(u); u.setId(null); //瞬時態
  • 持久(有id, 有關聯)=>游離(有id, 無關聯)
    獲得持久對象, 然后切斷持久對象與session的聯系
User u =(User) session.get(User.class, 1); session.close(); //或者 session.evict(u);
  • 游離(有id, 無關聯)=>瞬時(無id, 無關聯)
    設置游離對象id為null
User u = (User) session.get(User.class, 1); session.evict(u); u.setId(null);
  • 游離(有id, 無關聯)=>持久(有id, 有關聯)
    重新將游離對象寫入數據庫, 使得與session產生聯系
User u = (User) session.get(User.class, 1); session.evict(u); session.update(u); //寫入數據庫,變為持久態

注: 持久態的時候不允許修改持久化對象的id: u.setId(2), 修改直接報錯, 對持久化對象直接進行修改:u.setName(“C”), 那么Hibernate將會執行update操作, 將修改的持久化對象的數據同步到數據庫

一級緩存

一級緩存(session緩存), 用于存儲持久化對象 , 內部儲存結構是一個Map, 當需要使用持久化對象的時候, Hibernate會優先從緩存中獲取, 當session關閉, 一級緩存銷毀.

  • 快照
    快照: 緩存的復制品, 存放在session中
    快照主要用來與緩存中數據進行比較, 判斷緩存中數據是否發生變化
緩存中數據發生變化:Hibernate就會執行update, insert操作 緩存中數據沒有發生變化:說明數據庫中數據與緩存中數據相同,Hibernate就會避免主動執行SQL的update, insert, 減少資源浪費
  • 緩存執行過程:
查詢(select):session首次執行查詢, 發送select到數據庫, 將查詢的結果封裝放入session緩存,當再次執行查詢操作, Hibernate會先去Session緩存中查找,沒找到, 再去數據庫中查找. 更新(update):使用get/load執行查詢操作后, 持久化對象放入Session緩存中,然后對持久化對象update, 事務提交的時候將執行如下操作:先將Session緩存中修改后的對象與快照中數據進行比較,二者數據不相同的時候執行update, 數據更新到數據庫中二者數據相同, 那么不執行任何操作. 插入(insert):使用快照中數據與緩存中數據進行比較, 根據比較結果判斷是否繼續執行insert操作
  • 緩存刷新時機:
//查詢表中所有數據, 查詢后的結果會覆蓋緩存中數據 List<User> list = session.createQuery("from User").list();//執行flush, 刷新緩存中數據, session.flush();
  • 緩存導致的問題:
    每次獲取持久化對象, Hibernate優先去緩存中查找, 一定程度上提高了SQL的執行效率.
    緩存的存在, 出現的一個問題就是: 如果Hibernate獲得持久化對象后, 數據庫中數據又出現了修改, 當再次對該持久化對象進行操作的時候, Hibernate會優先從緩存中獲得持久化對象, 導致數據庫與Hibernate中數據不一致. 當出現這種問題的時候, 建議JDBC操作

注: 避免將相同的對象放入緩存中, 謹記緩存是一個Map, 看如下操作:

User u1 = session.get(User.class, 1);//緩存中放入u1 session.evict(u1);//變為游離態, 緩存中不存在 User u2 = session.get(User.class, 1);//獲得id=1的持久化對象,緩存中存在 session.update(u1);//u1重新變為持久態, 緩存中存在 //u1, u2同時存在緩存中, 將會報錯

session緩存常用API

  • evict(Object o): 將指定對象從session緩存中移除
  • clear(): 清除session緩存中所有的對象
  • refresh(Object o):
    強制刷新指定對象, 使持久化對象數據與數據庫中數據一致, 一定程度上避免session緩存產生的數據不一致問題;
    對o對象重新執行SQL
  • flush():
    對比快照與緩存中的數據, 確保數據一致, 然后將緩存中數據提交到數據庫, 類似于commit, 數據不一致的時候刷新緩存中的數據

對象的操作

  • save操作細節:
    當執行save的時候, 對象會從瞬時態=>持久態, 事務提交后將持久化對象存入數據庫中
執行save操作, 先根據映射文件中的主鍵生成策略生成主鍵. Hibernate將生成的主鍵賦值給瞬時態對象, 若該對象有id, 那么就會覆蓋原有的id 最后執行insert, 將瞬時態對象變為持久化對象
  • persist操作:
    persist操作與save一樣, 他們二者的區別在: persist會檢查對象主鍵, save不會檢查對象主鍵
    例如:
//u.setId(9); //save操作的時候Hibernate會自動進行主鍵生成,設置id無效 //persist會檢查Bean的id //發現與Hibernate主鍵生成策略不符, //報org.hibernate.PersistentObjectException: detached entity passed to persist: com.demo.User異常 ///將主鍵生成策略改為assigned或不設置主鍵,將不報錯u.setName("O");u.setPassword("66");//session.save(u);session.persist(u);
  • update操作細節:
    游離對象=>持久對象, 對持久對象屬性修改后, 使用save, 執行的是update, 而非insert
    在映射文件的class標簽中設置select-before-update=“true”, 那么執行update就會執行如下操作:
User u = new User(); u.setId(1); u.setName("P"); u.setPassword("pp"); session.update(u); //當執行這樣的操作的時候, 先執行select操作, 然后比較查詢結果 //與查詢結果一直, 那么就不執行update
  • saveOrUpdate:
    該方法就是save與update的結合, session.saveOrUpdate(u); 如果u存在id, 那么執行select, 然后再執行update, 沒有id, 執行insert

HQL, SQL, Criteria與緩存的聯系

下面通過例子說明:

體現一個問題: HQL都會執行select操作,將獲取的list與緩存中的數據進行比較//如果相同, 每次獲取緩存中的封裝對象/*List<User> list1 = session.createQuery("from User").list();List<User> list2 = session.createQuery("from User").list();List<User> list3 = session.createQuery("from User").list();for(User u:list1){System.out.println(u);}System.out.println(list1.hashCode()+"--"+list2.hashCode()+"--"+list3.hashCode());*///原生的SQL操作與HQL一致/*List<User> list1 = session.createSQLQuery("select * from t_User").addEntity(User.class).list();List<User> list2 = session.createSQLQuery("select * from t_User").addEntity(User.class).list();List<User> list3 = session.createSQLQuery("select * from t_User").addEntity(User.class).list();for(User u:list1){System.out.println(u);}System.out.println(list1.hashCode()+"--"+list2.hashCode()+"--"+list3.hashCode());*///criteria操作同上/*List<User> list1 = session.createCriteria(User.class).list();List<User> list2 = session.createCriteria(User.class).list();List<User> list3 = session.createCriteria(User.class).list();for(User u:list1){System.out.println(u);}System.out.println(list1.hashCode()+"--"+list2.hashCode()+"--"+list3.hashCode());*/

通過程序的運行觀察執行的SQL語句, 以及list對象的hashCode, 發現每次執行批量查詢(HQL, SQL, Criteria)都會select * from t_user, 然后將查詢的結果集與Session緩存中的數據進行比較.
說明:Hibernate把第一次執行的結果集放入緩存區, 在后面的查詢中, 盡管Hibernate發送了SQL語句, 但是使用的數據依舊是緩存中的數據, 這個時候使用get操作的時候, 獲取的數據也是從緩存區中得到

多表設計

表中存在的三種關系: 多對多, 一對多, 一對一

  • 數據庫描述上述關系:
    在數據庫中所有的關系都需要通過外鍵進行約束.

  • Bean對象描述上述的關系:

一對多:客戶與訂單 class Customer{//使用set描述客戶訂單列表private Set<Order> orderSet; } class Order{//多個商品訂單屬于一個客戶private Customer customer; }多對多: 學生與課程 class Student{private Set<Course> courseSet; } class Course{private Set<Student> StudentSet; }一對一: 學生與學生證 class Student{private StudentCard sc; } class StudentCard{private Student s; }

Hibernate的一對多關系實現

一對多操作的時候, 維護一個對象的時候會自動維護另一方的關系; 例如 Customer referenced Order, 當刪除Order的時候,Hibernate會先update商品表中所有的外鍵為null, 然后再執行刪除訂單操作, 我們就不用顯式修改商品表中的外鍵, 維護商品與訂單之間的關系

測試類:

//消費者: public class Customer {private Integer cid;private String cname;private Set<Order> orderSet = new HashSet<Order>();//get/set方法就不寫了 } //訂單 public class Order {private Integer oid;private String price;private Customer customer;//get/set方法就不寫了 }
  • Customer.hbm.xml與Order.hbm.xml編寫
Customer.hbm.xml <hibernate-mapping><class name="com.test.Customer" table="t_customer"><id><generator class="native"></generator></id><property name="cname"></property><!--配置一對多關系標簽--><set name="ordertest" cascade="save-update"><key column="customer_id"></key><one-to-many class="com.test.Order"/></set></class> </hibernate-mapping>Order.hbm.xml <hibernate-mapping><class name="com.test.Order" table="t_order"><id name="oid"><generator class="native"></generator></id><property name="price"></property><many-to-one name="customer" class="com.test.Customer" column="customer_id"></many-to-one></class> </hibernate-mapping>

在Customer.hbm.xml中:

set標簽用于確定容器(用于裝備Order)name屬性: 確定對象屬性名cascade屬性: 設置Customer與Order的級聯操作inverse屬性: 將關系的維護翻轉給對方, 默認值false(我維護這個關系)key標簽確定Customer主鍵名one-to-many標簽確定從表Order

cascade詳細級聯操作: 級聯操作就是, 當A與B綁定好關系后, 就比如Customer的set已經存儲了B, 當A執行save的時候, B也會自動執行save操作, 少寫session.save(B)的代碼, 同樣的也可以執行級聯刪除, 當A刪除了, B也跟著自動刪除
注: 級聯操作并不會維護關系

cascade的取值如下: save-update:級聯保存與修改A保存,同時保存B在程序中修改A中的B, 對應到數據庫中B將會級聯修改 delete:刪除A,同時刪除B,AB都不存在刪除過程中, 如果A在維護關系,那么A還會去處理外鍵,對外鍵設置為null,然后執行刪除.如果設置了inverse為true,A不去維護關系,A刪除,B就刪除,A不去update外鍵,減少了SQL操作 delete-orphan:孤兒刪除,解除關系,同時將B刪除,A存在的。接觸B與A的關系, 將B從A的集合內移除, B此時沒有引用對象, 就自動delete 如果需要配置多項,使用逗號分隔。<set cascade="save-update,delete">all : save-update 和 delete 整合 all-delete-orphan : 三個整合

此處注明: 千萬不要A設置了級聯刪除,然后B也設置了級聯刪除

當刪除B對象的時候, 由于級聯刪除, B會select所有A, 然后刪除A, 但是A又觸發級聯刪除, A會select所有的B, 最終刪除所有的B, 以及所有的A, 就因為刪除了一個B導致了如此嚴重的問題, 這個一定要避免!!!

在Order.hbm.xml中:

many-to-one標簽中name屬性: 確定屬性名稱class屬性: 確定參照的類column屬性: 確定Order表參照Customer表的外建名

往數據庫中保存Customer與Order:

Customer c = new Customer(); c.setName("tom"); Order o1 = new Order(); o1.setName("o1"); Order o2 = new Order(); o2.setName("o2"); //往c中添加Order信息, 維護關系 c.getOrderSet().add(o1);//Customer去維護, 執行update c.getOrderSet().add(o2);//執行update //往Order對象中添加Customer, 維護關系 o1.setCustomer(c);//Order去維護, 在insert中修改cid的值 o2.setCustomer(c); //保存到數據庫 session.save(c); session.save(o1); session.save(o2);

執行上面的代碼, Hibernate執行3次insert, 2次update, 需要注意的是在c, o1, o2 insert的過程中, 就已經在維護關系(對Order表的cid外鍵進行設置), 但是后面又對Order表執行了2次update, 產生的問題就是重復
所以通過上面的代碼也可以看出, 當維護關系的時候只需要維護一方, 另一方的關系就能得到維護

同理, 執行delete操作: session.delete?; 執行這條語句的時候, Hibernate會先將o1, o2的cid設置為null, 然后再對c進行delete, 從Customer的角度維護關系, 但是Customer不去維護關系的時候, 就需要遍歷Customer的orderSet, 將所有的Order對象setCustomer(null)主動切斷與Customer的關系, 設置所有Order的外鍵為null

總結: 設置一對多關系下, 可以只讓一方維護關系, 另一方不維護, 放棄維護關系的對象就是–非外鍵所在的對象, 就比如上面的操作, 讓Order 維護關系, Customer不去維護關系, 這種一方去維護關系也可以使用set標簽中的inverse屬性, 使得關系的維護交給對方

未完待續~~

上面有錯, 還請指出, 如果認為我寫的還不錯, 還請點個贊, 多多支持一下, O(∩_∩)O~~

總結

以上是生活随笔為你收集整理的Hibernate_2_Hibernate中对象状态及转化_一级缓存_Session详解_HQL/SQL/Criteria_一对多关系_级联操作的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。