轉載請注明: TheViperhttp://m.survivalescaperooms.com/TheViper
Hibernate對象狀態
瞬時(transient):由new操作符創建,且尚未Hibernate session關聯。瞬時對象不會被持久化到數據庫,也不會被賦予持久化標識。
持久(persistent):持久化的實例在數據庫中有對應的記錄,并擁有一個持久化標識。
持久化的實例可能是剛被保存,或剛被加載的,無論哪一種,它都只存在于相關聯的Session作用范圍內。這點很重要。Hibernate會檢測處于持久化狀態的對象的任何變動,在當前操作單元執行完畢時,將對對象數據與數據庫同步。
脫管(detached):在與持久化對象關聯的Session關閉后,對象就變成脫管狀態。
Hibernate的第一級緩存(Session緩存)
當調用Session的save(),update(),saveOrUpdate(),load()或get()方法,以及調用Query接口的list(),iterator()或filter()方法時,如果緩存中沒有相應的對象,Hibernate便會將對象添加到Session緩存中。
比如:
Transaction transaction = session.beginTransaction(); Feeling feeling = (Feeling) session.get(Feeling.class, 1); System.out.PRintln(feeling); System.out.println(feeling.getFeelingComments()); Feeling feeling1 = (Feeling) session.get(Feeling.class, 1); transaction.commit();
Hibernate: select feeling0_.feeling_id as feeling1_1_0_, feeling0_.content as content1_0_, feeling0_.id as id1_0_ from feeling feeling0_ where feeling0_.feeling_id =?model.Feeling@e34201Hibernate: select feelingcom0_.feeling_id as feeling3_1_1_, feelingcom0_.feelingComment_id as feelingC1_1_, feelingcom0_.feelingComment_id as feelingC1_2_0_, feelingcom0_.content as content2_0_, feelingcom0_.feeling_id as feeling3_2_0_, feelingcom0_.id as id2_0_ from feeling_comment feelingcom0_ where feelingcom0_.feeling_id=?[model.FeelingComment@39c71a, model.FeelingComment@11e8b08, model.FeelingComment@12fe3f3, model.FeelingComment@e91824]
可以看到第二次的get()并沒有生成sql.
可以使用1.evict()從緩存中清除指定的持久化對象。2.clear()清除所有。。。。多數情況下,不提倡通過上面兩個方法管理緩存。
當Session緩存中對象的屬性發生變化時,Session并不會立即清理緩存并執行相關的sql,使數據庫同步,而只是在特點的時間點才清理緩存。這使得Session可以把幾條相關的sql合并成一條,提升性能。
上面的特點的時間點包括:
1.調用Transaction的commit()方法。這時清理緩存,然后向數據庫提交事務。這樣做是為了最大限度減少數據庫的訪問和盡可能的縮短當前事務對數據庫中相關資源的鎖定時間。
2.執行一些查詢操作。這樣做是為了保證查詢返回的結果是即時,正確的。
3.顯式調用flush().注意,flush()不會提交事務,數據還有可能rollback.
對于flush()最常用的用法當然是批量操作了。比如批量插入100000條數據。
for(int i=1;i<100000;i++){ Customer c=new Customer(); session.save(c); if(i%20==0){ session.flush(); session.clear(); }}設置hibernate.jdbc.batch_size=20。一定數量(20)時強制刷出,使緩存與數據庫同步(數據寫入數據庫),然后清空所有緩存,避免內存溢出。
list()和iterator()
上面說了這兩個都會把結果添加到Session緩存。下面是不同點。
1.對待緩存的方式不一樣:如果配置了查詢緩存的話,list()會先去查詢緩存里面找,沒找到去數據庫找。注意,list()不會從Session緩存中找,也就是說下面的代碼并沒有減少sql語句。
Session session = sf.openSession(); Transaction transaction = session.beginTransaction(); Feeling feeling1 = (Feeling) session.load(Feeling.class, 1); System.out.println(feeling1); Query q = session .createQuery("from Feeling feeling where feeling.feeling_id=1"); List<Feeling> feeling = (List<Feeling>) q.list(); System.out.println(feeling); transaction.commit(); session.close();可以看到通過load()使feeling_id=1的Feeling對象被添加到了Session緩存。生成的sql.
Hibernate: select feeling0_.feeling_id as feeling1_1_0_, feeling0_.content as content1_0_, feeling0_.id as id1_0_ from feeling feeling0_ where feeling0_.feeling_id =?model.Feeling@1728f89Hibernate: select feeling0_.feeling_id as feeling1_1_, feeling0_.content as content1_, feeling0_.id as id1_ from feeling feeling0_ where feeling0_.feeling_id =1[model.Feeling@1728f89]
而iterate()是先從數據庫中查詢出所有滿足條件的數據索引,然后再根據這些數據索引進入一級和二級緩存進行匹配,如果對于數據索引有實體對象則直接返回該對象,如果沒有則在具體使用對象的時候才會進入數據庫加載數據,并且把數據索引和對于實體對象放進緩存。什么意思呢?看下面的代碼.
Session session = sf.openSession(); Transaction transaction = session.beginTransaction(); Query q = session .createQuery("from Feeling feeling where feeling.feeling_id=1"); Iterator<Feeling> feeling = (Iterator<Feeling>) q.iterate(); System.out.println(feeling.next()); transaction.commit(); session.close();Hibernate: select feeling0_.feeling_id as col_0_0_ from feeling feeling0_ where feeling0_.feeling_id =1Hibernate: select feeling0_.feeling_id as feeling1_1_0_, feeling0_.content as content1_0_, feeling0_.id as id1_0_ from feeling feeling0_ where feeling0_.feeling_id =?model.Feeling@11e3986
可以看到明明就是一個很簡單的查詢,iterate()都要把一句語句變成兩句 ,再看下面有Session緩存匹配的情況。
Session session = sf.openSession(); Transaction transaction = session.beginTransaction(); Feeling feeling1 = (Feeling) session.load(Feeling.class, 1); System.out.println(feeling1); Query q = session .createQuery("from Feeling feeling where feeling.feeling_id=1"); Iterator<Feeling> feeling = (Iterator<Feeling>) q.iterate(); System.out.println(feeling.next()); transaction.commit(); session.close();Hibernate: select feeling0_.feeling_id as feeling1_1_0_, feeling0_.content as content1_0_, feeling0_.id as id1_0_ from feeling feeling0_ where feeling0_.feeling_id =?model.Feeling@fcabd6Hibernate: select feeling0_.feeling_id as col_0_0_ from feeling feeling0_ where feeling0_.feeling_id =1model.Feeling@fcabd6
可以看到這次iterate()在查詢過數據索引后就沒有發出sql,而是直接用的上面的緩存。當然如果只是查詢某幾個字段,是不會去緩存中匹配的(默認設置)。
2.發出sql不一樣。很顯然,iterate()會產生N+1問題。
既然這樣,那iterate()適用于什么情況。
如果滿足這兩點,就可以用類似
select feeling0_.feeling_id as col_0_0_ from feeling feeling0_ where feeling0_.feeling_id =1這種只查詢了一個字段的語句查到對象的所有字段。
另外,iterate()返回的Itarator對象一直處于打開狀態,在以下情況被關閉。
load()和get()
http://m.survivalescaperooms.com/binjoo/articles/1621254.html
注意兩者的運行流程:get方法首先查詢session緩存,沒有的話查詢二級緩存,最后查詢數據庫;反而load方法創建時首先查詢session緩存,沒有就創建代理,實際使用數據時才查詢二級緩存和數據庫。
對load(),如果只是Feeling feeling = (Feeling) session.load(Feeling.class, 1);是不會發出sql的,只有像System.out.println(feeling);這樣使用了feeling才會發出。
load()只是生成類的動態代理,并沒有將數據庫的數據加載到相應的類中,所以適合在數據更新或為表插入外鍵數據時使用,
如hibernate查漏補缺1中的那個好友分類例子中,插入新加的好友并把他加入到已經存在的分類中。
Friend_Category fc = new Friend_Category(); fc.setCategory_name("已經存在的好友分類"); User_Friend_pk pk = new User_Friend_pk(); pk.setFriend_name(“新好友”); pk.setName("TheViper"); User_Friend uf = new User_Friend(); uf.setUser_friend_pk(pk); Friend_Category fc1 = (Friend_Category) session.load( Friend_Category.class, 1); uf.setFriend_categorys(fc1); // uf.setSort("好基友"); session.saveOrUpdate(fc1); session.save(uf);注意,這時對load的Friend_Category要用saveOrUpdate(),否則會出現異常org.hibernate.PersistentObjectException: uninitialized proxy passed to save()。
很多人都在用Spring的HibernateTemplate模板。它有個不好的地方,比如:
Feeling f = hibernateTemplate.load(Feeling.class, 1); System.out.println(f);
返回異常org.hibernate.LazyInitializationException: could not initialize proxy - no Session
原因在于HibernateTemplate和Spring的其他dao模板如JdbcTemplate一樣,走的是下面的流程:
1.準備資源->2.啟動->3.數據訪問->4.返回數據->5.提交/回滾事務->6.處理異常關閉資源
其中的3,4是我們寫的,其他的都是模板幫我們做的。也就是說,每次用了HibernateTemplate的方法后,HibernateTemplate都會自己關閉資源,防止數據庫連接,內存泄漏,而關閉的資源中就包括了Hibernate的Session.Session,自然就找不到Session緩存和類的動態代理(load()返回的)了。
或許是因為這個原因,在hibernate4中,與Spring集成時,Spring就沒有提供相應的模板了。
查詢緩存
如果啟用,當第一次執行查詢語句時,hibernate會把查詢的結果存放在二級緩存中。注意,如果查詢結果中有實體類,則查詢緩存只會保存實體類的id.比如,
Session session = sf.openSession(); Transaction transaction = session.beginTransaction(); Query q = session .createQuery("from Feeling feeling"); q.setCacheable(true); List<Feeling> feeling1 = (List<Feeling>) q.list(); System.out.println(feeling1); transaction.commit(); session.close(); Session session1 = sf.openSession(); Transaction transaction1 = session1.beginTransaction(); Query q1 = session1 .createQuery("from Feeling feeling"); q1.setCacheable(true); List<Feeling> feeling = (List<Feeling>) q1.list(); System.out.println(feeling); transaction1.commit(); session1.close();Hibernate: select feeling0_.feeling_id as feeling1_1_, feeling0_.content as content1_, feeling0_.id as id1_ from feeling feeling0_[model.Feeling@114b355, model.Feeling@113925a, model.Feeling@1ed5550]Hibernate: select feeling0_.feeling_id as feeling1_1_0_, feeling0_.content as content1_0_, feeling0_.id as id1_0_ from feeling feeling0_ where feeling0_.feeling_id =?Hibernate: select feeling0_.feeling_id as feeling1_1_0_, feeling0_.content as content1_0_, feeling0_.id as id1_0_ from feeling feeling0_ where feeling0_.feeling_id =?Hibernate: select feeling0_.feeling_id as feeling1_1_0_, feeling0_.content as content1_0_, feeling0_.id as id1_0_ from feeling feeling0_ where feeling0_.feeling_id =?[model.Feeling@1000db5, model.Feeling@1f28c, model.Feeling@1c64fff]
這里我沒有對Feeling的映射文件設置類的二級緩存,可以看到第二次是把保存的feeling_id取出進行查詢的,而不是緩存了實體類的所有屬性。
如果把Feeling類的二級緩存打開,就能看到第二次查詢不會有sql出現.說明查詢緩存如果要使用,應該與二級緩存一起使用。
Hibernate: select feeling0_.feeling_id as feeling1_1_, feeling0_.content as content1_, feeling0_.id as id1_ from feeling feeling0_[model.Feeling@80da83, model.Feeling@1520c38, model.Feeling@1a05d51][model.Feeling@176d5d3, model.Feeling@16d471b, model.Feeling@1dee8d0]
還有一點,只配置了hibernate.cache.use_query_cache=true的話,還是不會用查詢緩存。必須在調用Query接口時query.setCacheable(true);.
Cascade
在<set>,<one-to-one>,<many-to-one>(個人覺得在many-to-one里面很沒用)中使用。

Hibernate日志

Hibernate第二級緩存
進程或集群范圍內的緩存,SessionFactory級別,而第一級緩存是事務范圍的緩存。第二級緩存中存放的是對象的散裝數據。
第二級緩存的4種并發訪問策略,每一種策略對應一種事務隔離級別。
transactional隔離級別最高,并發性能最低;read-only則反之。
緩存插件可分為進程范圍內緩存(EHCache,OSCache),集群范圍內緩存(SwarmCache,JbossCache).其中SwarmCache不支持查詢緩存。

配置hibernate.cfg.xml
<property name="hibernate.cache.use_second_level_cache">true</property> <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
Hibernate允許在類和集合上配置二級緩存,在<class>和<set>上都有<cache>子元素.
用法:<cache usage="transactional|read-write|nonstrict-read-write|read-only" region="regionName" include="all|non-lazy"/>
usage:指定并發訪問策略。region:指定二級緩存的區域名字,默認為類或集合的名字。include:指定當緩存一個對象時,會不會緩存它的映射為延遲加載的屬性。默認all.
ehcache配置

配置<cache>的時候有一點要注意,在<set>下配置了<cache>的話還需要在<set>的另一邊的<class>下也配置<cache>才會讓二級緩存對集合有效。
管理二級緩存
sessionFactory.evict(Category.class,new Long(1));//清除二級緩存中id為1的Category對象
sessionFactory.evict(”mypack.Category“);//清除二級緩存中Category類的所有對象
sessionFactory.evictCollection(”mypack.Category.items“);//清除二級緩存中Category類的所有對象的items集合
Session與二級緩存的交互模式:
用org.hibernate.CacheMode的5個靜態常量表示。
用法:session.setCacheMode(CacheMode.IGNORE);
新聞熱點
疑難解答