国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

hibernate查漏補缺2

2019-11-14 22:00:08
字體:
來源:轉載
供稿:網友
hibernate查漏補缺2

轉載請注明: 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對象一直處于打開狀態,在以下情況被關閉。

  • 遍歷訪問完結果集中的所有對象
  • 關閉Session
  • 通過org.hibernate.Hibernate.close(iterator)方法關閉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):僅在受管理的環境中適應。提供repeatable read事務隔離級別,適用于經常訪問但很少被修改的數據,可防止臟讀和不可重復讀這類并發問題。
  • 讀寫型(read-write):read committed事務隔離級別,僅在非集群的環境中適用。適用于經常訪問但很少被修改的數據,防止臟讀。
  • 非嚴格讀寫型(nonstrict-read-write):不保證緩存與數據庫中數據的一致性。如果兩個事務訪問緩存中的相同數據,則必須為該數據一個很短的數據過期時間,以避免臟讀。適用于極少被修改且允許偶爾臟讀的數據這種情況。
  • 只讀型(read-only):適用于從來不會修改的數據。

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個靜態常量表示。

  • CacheMode.NORMAL:正常模式(默認),Session會向二級緩存中讀取和寫入數據。
  • CacheMode.IGNORE:忽略模式,Session不會向二級緩存讀取,也不會寫入數據。
  • CacheMode.GET:Session對二級緩存只讀不寫。
  • CacheMode.PUT:Session對二級緩存只寫不對。
  • CacheMode.REFRESH:刷新模式,Session不會從二級緩存中讀取數據,但會向其中寫入從數據庫讀取的數據。和PUT的區別在于REFRESH會忽略配置文件中的hibernate.cache.use_minimal_puts屬性,強制刷新二級緩存中的所有數據。

用法:session.setCacheMode(CacheMode.IGNORE);


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 崇州市| 荃湾区| 布尔津县| 科技| 阜南县| 凤庆县| 扎囊县| 鹤庆县| 平泉县| 达拉特旗| 西乌珠穆沁旗| 石嘴山市| 囊谦县| 保定市| 拉萨市| 都江堰市| 金沙县| 浑源县| 武穴市| 牙克石市| 北海市| 灵石县| 威海市| 太白县| 卓资县| 荥阳市| 永靖县| 武宣县| 秦安县| 嵊州市| 澄迈县| 武胜县| 贵德县| 湘乡市| 廉江市| 邵武市| 青铜峡市| 菏泽市| 乃东县| 永安市| 西林县|