tbwshc

          Hibernate 所有緩存機(jī)制詳解

          hibernate提供的一級緩存

          hibernate是一個線程對應(yīng)一個session,一個線程可以看成一個用戶。也就是說session級緩存(一級緩存)只能給一個線程用,別的線程用不了,一級緩存就是和線程綁定了。

          hibernate一級緩存生命周期很短,和session生命周期一樣,一級緩存也稱session級的緩存或事務(wù)級緩存。如果tb事務(wù)提交或回滾了,我們稱session就關(guān)閉了,生命周期結(jié)束了。

          緩存和連接池的區(qū)別緩存和池都是放在內(nèi)存里,實現(xiàn)是一樣的,都是為了提高性能的。但有細(xì)微的差別,池是重量級的,里面的數(shù)據(jù)是一樣的,比如一個池里放100個Connection連接對象,這個100個都是一樣的。緩存里的數(shù)據(jù),每個都不一樣。比如讀取100條數(shù)據(jù)庫記錄放到緩存里,這100條記錄都不一樣。

          緩存主要是用于查詢

          //同一個session中,發(fā)出兩次load方法查詢

          Student student = (Student)session.load(Student.class, 1);

          System.out.println("student.name=" + student.getName());

          //不會發(fā)出查詢語句,load使用緩存

          student = (Student)session.load(Student.class, 1);

          System.out.println("student.name=" + student.getName());

          第二次查詢第一次相同的數(shù)據(jù),第二次load方法就是從緩存里取數(shù)據(jù),不會發(fā)出sql語句到數(shù)據(jù)庫里查詢。

          //同一個session,發(fā)出兩次get方法查詢

          Student student = (Student)session.get(Student.class, 1);

          System.out.println("student.name=" + student.getName());

          //不會發(fā)出查詢語句,get使用緩存

          student = (Student)session.get(Student.class, 1);

          System.out.println("student.name=" + student.getName());

          第二次查詢第一次相同的數(shù)據(jù),第二次不會發(fā)出sql語句查詢數(shù)據(jù)庫,而是到緩存里取數(shù)據(jù)。

          //同一個session,發(fā)出兩次iterate查詢實體對象

          Iterator iter = session.createQuery

          ("from Student s where s.id<5").iterate();

          while (iter.hasNext()) {

          Student student = (Student)iter.next();

          System.out.println(student.getName());

          }

          System.out.println("--------------------------------------");

          //它會發(fā)出查詢id的語句,但不會發(fā)出根據(jù)id查詢學(xué)生的語句,因為iterate使用緩存

          iter = session.createQuery("from Student s where s.id<5").iterate();

          while (iter.hasNext()) {

          Student student = (Student)iter.next();

          System.out.println(student.getName());

          }

          一說到iterater查詢就要立刻想起:iterater查詢在沒有緩存的情況下會有N+1的問題。

          執(zhí)行上面代碼查看控制臺的sql語句,第一次iterate查詢會發(fā)出N+1條sql語句,第一條sql語句查詢所有的id,然后根據(jù)id查詢實體對象,有N個id就發(fā)出N條語句查詢實體。

          第二次iterate查詢,卻只發(fā)一條sql語句,查詢所有的id,然后根據(jù)id到緩存里取實體對象,不再發(fā)sql語句到數(shù)據(jù)庫里查詢了。

          //同一個session,發(fā)出兩次iterate查詢,查詢普通屬性

          Iterator iter = session.createQuery(

          "select s.name from Student s where s.id<5").iterate();

          while (iter.hasNext()) {

          String name = (String)iter.next();

          System.out.println(name);

          }

          System.out.println("--------------------------------------");

          //iterate查詢普通屬性,一級緩存不會緩存,所以發(fā)出查詢語句

          //一級緩存是緩存實體對象的

          iter = session.createQuery

          ("select s.name from Student s where s.id<5").iterate();

          while (iter.hasNext()) {

          String name = (String)iter.next();

          System.out.println(name);

          }

          執(zhí)行代碼看控制臺sql語句,第一次發(fā)出N+1條sql語句,第二次還是發(fā)出了N+1條sql語句。因為一級緩存只緩存實體對象tb不會緩存普通屬性,所以第二次還是發(fā)出sql查詢語句。

          //兩個session,每個session發(fā)出一個load方法查詢實體對象

          try {

          session = HibernateUtils.getSession();

          session.beginTransaction();

          Student student = (Student)session.load(Student.class, 1);

          System.out.println("student.name=" + student.getName());

          session.getTransaction().commit();

          }catch(Exception e) {

          e.printStackTrace();

          session.getTransaction().rollback();

          }finally {

          HibernateUtils.closeSession(session);

          }

          第二個session調(diào)用load方法

          try {

          session = HibernateUtils.getSession();

          session.beginTransaction();

          Student student = (Student)session.load(Student.class, 1);

          //會發(fā)出查詢語句,session間不能共享一級緩存數(shù)據(jù)

          //因為他會伴隨著session的消亡而消亡

          System.out.println("student.name=" + student.getName());

          session.getTransaction().commit();

          }catch(Exception e) {

          e.printStackTrace();

          session.getTransaction().rollback();

          }finally {

          HibernateUtils.closeSession(session);

          }

          第一個session的load方法會發(fā)出sql語句查詢實體對象,第二個session的load方法也會發(fā)出sql語句查詢實體對象。因為session間不能共享一級緩存的數(shù)據(jù),所以第二個session的load方法查詢相同的數(shù)據(jù)還是要到數(shù)據(jù)庫中查詢,因為它找不到第一個session里緩存的數(shù)據(jù)。

          //同一個session,先調(diào)用save方法再調(diào)用load方法查詢剛剛save的數(shù)據(jù)

          Student student = new Student();

          student.setName("張三");

          //save方法返回實體對象的id

          Serializable id = session.save(student);

          student = (Student)session.load(Student.class, id);

          //不會發(fā)出查詢語句,因為save支持緩存

          System.out.println("student.name=" + student.getName());

          先save保存實體對象,再用load方法查詢剛剛save的實體對象,則load方法不會發(fā)出sql語句到數(shù)據(jù)庫查詢的,而是到緩存里取數(shù)據(jù),因為save方法也支持緩存。當(dāng)然前提是同一個session。

          //大批量的數(shù)據(jù)添加

          for (int i=0; i<100; i++) {

          Student student = new Student();

          student.setName("張三" + i);

          session.save(student);

          //每20條更新一次

          if (i % 20 == 0) {

          session.flush();

          //清除緩存的內(nèi)容

          session.clear();

          }

          }

          大批量數(shù)據(jù)添加時,會造成內(nèi)存溢出的,因為save方法支持緩存,每save一個對象就往緩存里放,如果對象足夠多內(nèi)存肯定要溢出。一般的做法是先判斷一下save了多少個對象,如果save了20個對象就對緩存手動的清理緩存,這樣就不會造成內(nèi)存溢出。

          注意:清理緩存前,要手動調(diào)用flush方法同步到數(shù)據(jù)庫,否則save的對象就沒有保存到數(shù)據(jù)庫里。

          注意:大批量數(shù)據(jù)的添加還是不要使用hibernate,這是hibernate弱項。可以使用jdbc(速度也不會太快,只是比hibernate好一點),或者使用工具產(chǎn)品來實現(xiàn),比如oracle的Oracle SQL Loader,導(dǎo)入數(shù)據(jù)特別快。 



          Hibernate 二級緩存

          二級緩存需要sessionFactory來管理,它是進(jìn)初級的緩存,所有人都可以使用,它是共享的。

          二級緩存比較復(fù)雜,一般用第三方產(chǎn)品。hibernate提供了一個簡單實現(xiàn),用Hashtable做的,只能作為我們的測試使用,商用還是需要第三方產(chǎn)品。

          使用緩存,肯定是長時間不改變的數(shù)據(jù),如果經(jīng)常變化的數(shù)據(jù)放到緩存里就沒有太大意義了。因為經(jīng)常變化,還是需要經(jīng)常到數(shù)據(jù)庫里查詢,那就沒有必要用緩存了。

          hibernate做了一些優(yōu)化,和一些第三方的緩存產(chǎn)品做了集成。老師采用EHCache緩存產(chǎn)品。

          和EHCache二級緩存產(chǎn)品集成:EHCache的jar文件在hibernate的lib里,我們還需要設(shè)置一系列的緩存使用策略,需要一個配置文件ehcache.xml來配置。這個文件放在類路徑下。

          //默認(rèn)配置,所有的類都遵循這個配置

          <defaultCache

                  //緩存里可以放10000個對象

                  maxElementsInMemory="10000"

                  //過不過期,如果是true就是永遠(yuǎn)不過期

                  eternal="false"

                  //一個對象被訪問后多長時間還沒有訪問就失效(120秒還沒有再次訪問就失效)

                  timeToIdleSeconds="120"

                  //對象存活時間(120秒),如果設(shè)置永不過期,這個就沒有必要設(shè)了

                  timeToLiveSeconds="120"

                  //溢出的問題,如果設(shè)成true,緩存里超過10000個對象就保存到磁盤里

                  overflowToDisk="true"

                  />

          我們也可以對某個對象單獨配置:

          <cache name="com.bjpowernode.hibernate.Student"

                  maxElementsInMemory="100"

                  eternal="false"

                  timeToIdleSeconds="10000"

                  timeToLiveSeconds="10000"

                  overflowToDisk="true"

                  />

          還需要在hibernate.cfg.xml配置文件配置緩存,讓hibernate知道我們使用的是那個二級緩存。

          <!-- 配置緩存提供商 -->

          <property name="hibernate.cache.provider_class">

          org.hibernate.cache.EhCacheProvider</property>

          <!-- 啟用二級緩存,這也是它的默認(rèn)配置 -->

          <property name="hibernate.cache.use_second_level_cache">

          true</property>

          啟用二級緩存的配置可以不寫的,因為默認(rèn)就是true開啟二級緩存。

          必須還手動指定那些實體類的對象放到緩存里在hibernate.cfg.xml里:

          //在<sessionfactory>標(biāo)簽里,在<mapping>標(biāo)簽后配置

          <class-cache class="com.bjpowernode.hibernate.Student"

          usage="read-only"/>

          或者在實體類映射文件里:

          //在<class>標(biāo)簽里,<id>標(biāo)簽前配置

          <cache usage="read-only"/>

          usage屬性表示使用緩存的策略,一般優(yōu)先使用read-only,表示如果這個數(shù)據(jù)放到緩存里了,則不允許修改,如果修改就會報錯。這就要注意我們放入緩存的數(shù)據(jù)不允許修改。因為放緩存里的數(shù)據(jù)經(jīng)常修改,也就沒有必要放到緩存里。

          使用read-only策略效率好,因為不能改緩存。但是可能會出現(xiàn)臟數(shù)據(jù)的問題,這個問題解決方法只能依賴緩存的超時,比如上面我們設(shè)置了超時為120秒,120后就可以對緩存里對象進(jìn)行修改,而在120秒之內(nèi)訪問這個對象可能會查詢臟數(shù)據(jù)的問題,因為我們修改對象后數(shù)據(jù)庫里改變了,而緩存卻不能改變,這樣造成數(shù)據(jù)不同步,也就是臟數(shù)據(jù)的問題。

          第二種緩存策略read-write,當(dāng)持久對象發(fā)生變化,緩存里就會跟著變化,數(shù)據(jù)庫中也改變了。這種方式需要加解鎖,效率要比第一種慢。

          還有兩種策略,請看hibernate文檔,最常用還是第一二種策略。

          二級緩存測試代碼演示:注意上面我們講的兩個session分別調(diào)用load方法查詢相同的數(shù)據(jù),第二個session的load方法還是發(fā)了sql語句到數(shù)據(jù)庫查詢數(shù)據(jù),這是因為一級緩存只在當(dāng)前session中共享,也就是說一級緩存不能跨session訪問。

          //開啟二級緩存,二級緩存是進(jìn)程級的緩存,可以共享

          //兩個session分別調(diào)用load方法查詢相同的實體對象

          try {

          session = HibernateUtils.getSession();

          session.beginTransaction();

          Student student = (Student)session.load(Student.class, 1);

          System.out.println("student.name=" + student.getName());

          session.getTransaction().commit();

          }catch(Exception e) {

          e.printStackTrace();

          session.getTransaction().rollback();

          }finally {

          HibernateUtils.closeSession(session);

          }

          try {

          session = HibernateUtils.getSession();

          session.beginTransaction();

          Student student = (Student)session.load(Student.class, 1);

          //不會發(fā)出查詢語句,因為配置二級緩存,session可以共享二級緩存中的數(shù)據(jù)

          //二級緩存是進(jìn)程級的緩存

          System.out.println("student.name=" + student.getName());

          session.getTransaction().commit();

          }catch(Exception e) {

          e.printStackTrace();

          session.getTransaction().rollback();

          }finally {

          HibernateUtils.closeSession(session);

          }

          如果開啟了二級緩存,那么第二個session調(diào)用的load方法查詢第一次查詢的數(shù)據(jù),是不會發(fā)出sql語句查詢數(shù)據(jù)庫的,而是去二級緩存中取數(shù)據(jù)。

          //開啟二級緩存

          //兩個session分別調(diào)用get方法查詢相同的實體對象

          try {

          session = HibernateUtils.getSession();

          session.beginTransaction();

          Student student = (Student)session.get(Student.class, 1);

          System.out.println("student.name=" + student.getName());

          session.getTransaction().commit();

          }catch(Exception e) {

          e.printStackTrace();

          session.getTransaction().rollback();

          }finally {

          HibernateUtils.closeSession(session);

          }

          try {

          session = HibernateUtils.getSession();

          session.beginTransaction();

          Student student = (Student)session.get(Student.class, 1);

          //不會發(fā)出查詢語句,因為配置二級緩存,session可以共享二級緩存中的數(shù)據(jù)

          //二級緩存是進(jìn)程級的緩存

          System.out.println("student.name=" + student.getName());

          session.getTransaction().commit();

          }catch(Exception e) {

          e.printStackTrace();

          session.getTransaction().rollback();

          }finally {

          HibernateUtils.closeSession(session);

          }

          注意:二級緩存必須讓sessionfactory管理,讓sessionfactory來清除二級緩存。sessionFactory.evict(Student.class);//清除二級緩存中所有student對象,sessionFactory.evict(Student.class,1);//清除二級緩存中id為1的student對象。

          如果在第一個session調(diào)用load或get方法查詢數(shù)據(jù)后,把二級緩存清除了,那么第二個session調(diào)用load或get方法查詢相同的數(shù)據(jù)時,還是會發(fā)出sql語句查詢數(shù)據(jù)庫的,因為緩存里沒有數(shù)據(jù)只能到數(shù)據(jù)庫里查詢。

          我們查詢數(shù)據(jù)后會默認(rèn)自動的放到二級和一級緩存里,如果我們想查詢的數(shù)據(jù)不放到緩存里,也是可以的。也就是說我們可以控制一級緩存和二級緩存的交換。

          session.setCacheMode(CacheMode.IGNORE);禁止將一級緩存中的數(shù)據(jù)往二級緩存里放。

          還是用上面代碼測試,在第一個session調(diào)用load方法前,執(zhí)行session.setCacheMode(CacheMode.IGNORE);這樣load方法查詢的數(shù)據(jù)不會放到二級緩存里。那么第二個session執(zhí)行l(wèi)oad方法查詢相同的數(shù)據(jù),會發(fā)出sql語句到數(shù)據(jù)庫中查詢,因為二級緩存里沒有數(shù)據(jù),一級緩存因為不同的session不能共享,所以只能到數(shù)據(jù)庫里查詢。

          上面我們講過大批量的數(shù)據(jù)添加時可能會出現(xiàn)溢出,解決辦法是每當(dāng)天就20個對象后就清理一次一級緩存。如果我們使用了二級緩存,光清理一級緩存是不夠的,還要禁止一二級緩存交互,在save方法前調(diào)用session.setCacheMode(CacheMode.IGNORE)。

          二級緩存也不會存放普通屬性的查詢數(shù)據(jù),這和一級緩存是一樣的,只存放實體對象。session級的緩存對性能的提高沒有太大的意義,因為生命周期太短了。



          Hibernate 查詢緩存

          一級緩存和二級緩存都只是存放實體對象的,如果查詢實體對象的普通屬性的數(shù)據(jù),只能放到查詢緩存里,查詢緩存還存放查詢實體對象的id。

          查詢緩存的生命周期不確定,當(dāng)它關(guān)聯(lián)的表發(fā)生修改,查詢緩存的生命周期就結(jié)束。這里表的修改指的是通過hibernate修改,并不是通過數(shù)據(jù)庫客戶端軟件登陸到數(shù)據(jù)庫上修改。

          hibernate的查詢緩存默認(rèn)是關(guān)閉的,如果要使用就要到hibernate.cfg.xml文件里配置:

          <property name="hibernate.cache.use_query_cache">true</property>

          并且必須在程序中手動啟用查詢緩存,在query接口中的setCacheable(true)方法來啟用。

          //關(guān)閉二級緩存,沒有開啟查詢緩存,采用list方法查詢普通屬性

          //同一個sessin,查詢兩次

          List names = session.createQuery("select s.name from Student s")

          .list();

          for (int i=0; i<names.size(); i++) {

          String name = (String)names.get(i);

          System.out.println(name);

          }

          System.out.println("-----------------------------------------");

          //會發(fā)出sql語句

          names = session.createQuery("select s.name from Student s")

          .setCacheable(true)

          .list();

          for (int i=0; i<names.size(); i++) {

          String name = (String)names.get(i);

          System.out.println(name);

          }

          上面代碼運行,由于沒有使用查詢緩存,而一、二級緩存不會緩存普通屬性,所以第二次查詢還是會發(fā)出sql語句到數(shù)據(jù)庫中查詢。

          現(xiàn)在開啟查詢緩存,關(guān)閉二級緩存,并且在第一次的list方法前調(diào)用setCacheable(true),并且第二次list查詢前也調(diào)用這句代碼,可以寫出下面這樣:

          List names = session.createQuery("select s.name from Student s")

          .setCacheable(true)

          .list();

          其它代碼不變,運行代碼后發(fā)現(xiàn)第二次list查詢普通屬性沒有發(fā)出sql語句,也就是說沒有到數(shù)據(jù)庫中查詢,而是到查詢緩存中取數(shù)據(jù)。

          //開啟查詢緩存,關(guān)閉二級緩存,采用list方法查詢普通屬性

          //在兩個session中調(diào)用list方法

          try {

          session = HibernateUtils.getSession();

          session.beginTransaction();

          List names = session.createQuery("select s.name from Student s")

          .setCacheable(true)

          .list();

          for (int i=0; i<names.size(); i++) {

          String name = (String)names.get(i);

          System.out.println(name);

          }

          session.getTransaction().commit();

          }catch(Exception e) {

          e.printStackTrace();

          session.getTransaction().rollback();

          }finally {

          HibernateUtils.closeSession(session);

          }

          System.out.println("----------------------------------------");

          try {

          session = HibernateUtils.getSession();

          session.beginTransaction();

          //不會發(fā)出查詢語句,因為查詢緩存和session的生命周期沒有關(guān)系

          List names = session.createQuery("select s.name from Student s")

          .setCacheable(true)

          .list();

          for (int i=0; i<names.size(); i++) {

          String name = (String)names.get(i);

          System.out.println(name);

          }

          session.getTransaction().commit();

          }catch(Exception e) {

          e.printStackTrace();

          session.getTransaction().rollback();

          }finally {

          HibernateUtils.closeSession(session);

          }

          運行結(jié)果是第二個session發(fā)出的list方法查詢普通屬性,沒有發(fā)出sql語句到數(shù)據(jù)庫中查詢,而是到查詢緩存里取數(shù)據(jù),這說明查詢緩存和session生命周期沒有關(guān)系

          //開啟緩存,關(guān)閉二級緩存,采用iterate方法查詢普通屬性

          //在兩個session中調(diào)用iterate方法查詢

          運行結(jié)果是第二個session的iterate方法還是發(fā)出了sql語句查詢數(shù)據(jù)庫,這說明iterate迭代查詢普通屬性不支持查詢緩存

          //關(guān)閉查詢緩存,關(guān)閉二級緩存,采用list方法查詢實體對象

          //在兩個session中調(diào)用list方法查詢

          運行結(jié)果第一個session調(diào)用list方法查詢實體對象會發(fā)出sql語句查詢數(shù)據(jù),因為關(guān)閉了二級緩存,所以第二個session調(diào)用list方法查詢實體對象,還是會發(fā)出sql語句到數(shù)據(jù)庫中查詢。

          //開啟查詢緩存,關(guān)閉二級緩存

          //在兩個session中調(diào)用list方法查詢實體對象

          運行結(jié)果第一個session調(diào)用list方法查詢實體對象會發(fā)出sql語句查詢數(shù)據(jù)庫的。第二個session調(diào)用list方法查詢實體對象,卻發(fā)出了很多sql語句查詢數(shù)據(jù)庫,這跟N+1的問題是一樣的,發(fā)出了N+1條sql語句。為什么會出現(xiàn)這樣的情況呢?這是因為我們現(xiàn)在查詢的是實體對象,查詢緩存會把第一次查詢的實體對象的id放到緩存里,當(dāng)?shù)诙€session再次調(diào)用list方法時,它會到查詢緩存里把id一個一個的拿出來,然后到相應(yīng)的緩存里找(先找一級緩存找不到再找二級緩存),如果找到了就返回,如果還是沒有找到,則會根據(jù)一個一個的id到數(shù)據(jù)庫中查詢,所以一個id就會有一條sql語句。

          注意:如果配置了二級緩存,則第一次查詢實體對象后,會往一級緩存和二級緩存里都存放。如果沒有二級緩存,則只在一級緩存里存放。(一級緩存不能跨session共享)

          //開啟查詢緩存,開啟二級緩存

          //在兩個session中調(diào)用list方法查詢實體對象

          運行結(jié)果是第一個session調(diào)用list方法會發(fā)出sql語句到數(shù)據(jù)庫里查詢實體對象,因為配置了二級緩存,則實體對象會放到二級緩存里,因為配置了查詢緩存,則實體對象所有的id放到了查詢緩存里。第二個session調(diào)用list方法不會發(fā)出sql語句,而是到二級緩存里取數(shù)據(jù)。

          查詢緩存意義不大,查詢緩存說白了就是存放由list方法或iterate方法查詢的數(shù)據(jù)。我們在查詢時很少出現(xiàn)完全相同條件的查詢,這也就是命中率低,這樣緩存里的數(shù)據(jù)總是變化的,所以說意義不大。除非是多次查詢都是查詢相同條件的數(shù)據(jù),也就是說返回的結(jié)果總是一樣,這樣配置查詢緩存才有意義。

          posted on 2012-06-05 12:44 chen11-1 閱讀(59623) 評論(24)  編輯  收藏 所屬分類: java

          Feedback

          # re: Hibernate 所有緩存機(jī)制詳解 2013-11-01 13:56 叮叮當(dāng)當(dāng)

          寫的很不錯,贊   回復(fù)  更多評論   

          # re: Hibernate 所有緩存機(jī)制詳解 2013-12-12 18:34 hubery

          不錯,寫得很詳細(xì),很好,是很好的學(xué)習(xí)榜樣!!!  回復(fù)  更多評論   

          # re: Hibernate 所有緩存機(jī)制詳解 2014-01-14 16:30 我來了

          頂起,就是有些多,頭大  回復(fù)  更多評論   

          # re: Hibernate 所有緩存機(jī)制詳解 2014-02-14 10:05 很快解決

          就會更好  回復(fù)  更多評論   

          # re: Hibernate 所有緩存機(jī)制詳解 2014-02-28 09:49 hiber

          很不錯  回復(fù)  更多評論   

          # re: Hibernate 所有緩存機(jī)制詳解 2014-03-05 13:21 rlf

          寫的很好。一般人都能看懂!   回復(fù)  更多評論   

          # re: Hibernate 所有緩存機(jī)制詳解 2014-04-03 18:47 無名氏

          good  回復(fù)  更多評論   

          # re: Hibernate 所有緩存機(jī)制詳解 2014-04-07 20:50 wanjinxiong

          是不是寫錯了,上面所說的一級緩存一個說第二次查詢是到緩存中取數(shù)據(jù)!下面怎么又說第二次查詢還是要到數(shù)據(jù)庫中取。因為session間不能共享一級緩存的數(shù)據(jù),所以第二個session的load方法查詢相同的數(shù)據(jù)還是要到數(shù)據(jù)庫中查詢,因為它找不到第一個session里緩存的數(shù)據(jù)。  回復(fù)  更多評論   

          # re: Hibernate 所有緩存機(jī)制詳解 2014-04-10 18:28 路人甲

          ~\(≧▽≦)/~贊  回復(fù)  更多評論   

          # re: Hibernate 所有緩存機(jī)制詳解 2014-05-21 17:34 renhongjin

          不錯  回復(fù)  更多評論   

          # re: Hibernate 所有緩存機(jī)制詳解 2014-05-27 11:15 阿濤

          很好!很強(qiáng)大啊.  回復(fù)  更多評論   

          # re: Hibernate 所有緩存機(jī)制詳解 2014-06-27 15:37 瘋癲偉

          很給力、、、  回復(fù)  更多評論   

          # re: Hibernate 所有緩存機(jī)制詳解 2014-06-29 16:25 淡定點

          好  回復(fù)  更多評論   

          # re: Hibernate 所有緩存機(jī)制詳解 2014-07-11 16:37 小濤

          帥、、、簡單易懂、、  回復(fù)  更多評論   

          # re: Hibernate 所有緩存機(jī)制詳解[未登錄] 2014-07-26 19:24 xx

          不錯  回復(fù)  更多評論   

          # re: Hibernate 所有緩存機(jī)制詳解[未登錄] 2014-09-09 09:50 1

          ryy  回復(fù)  更多評論   

          # re: Hibernate 所有緩存機(jī)制詳解[未登錄] 2014-09-11 12:02 海泉

          講解很詳細(xì),對緩存機(jī)制理解更深,3q  回復(fù)  更多評論   

          # re: Hibernate 所有緩存機(jī)制詳解[未登錄] 2014-09-24 09:50 ray

          很棒!  回復(fù)  更多評論   

          # re: Hibernate 所有緩存機(jī)制詳解[未登錄] 2014-10-28 14:16 xx

          非常的不錯  回復(fù)  更多評論   

          # re: Hibernate 所有緩存機(jī)制詳解 2015-02-06 15:07 iechenyb

          真不錯,學(xué)習(xí)拉!  回復(fù)  更多評論   

          # re: Hibernate 所有緩存機(jī)制詳解 2015-03-06 11:34 everyBody

          感謝  回復(fù)  更多評論   

          # re: Hibernate 所有緩存機(jī)制詳解 2015-06-02 17:40 kennero

          是查詢緩存,不是一二級緩存  回復(fù)  更多評論   

          # re: Hibernate 所有緩存機(jī)制詳解[未登錄] 2015-09-27 21:11 微笑

          太厲害了,寫的太好了~  回復(fù)  更多評論   

          # re: Hibernate 所有緩存機(jī)制詳解 2015-12-04 11:39 和大象掰腕子

          神州行 我看行  回復(fù)  更多評論   

          主站蜘蛛池模板: 新晃| 交城县| 松阳县| 郁南县| 吉木乃县| 辽宁省| 建阳市| 江津市| 广德县| 齐河县| 山东| 昌黎县| 淄博市| 资兴市| 浦县| 华宁县| 宝鸡市| 乌兰县| 宁晋县| 乌拉特前旗| 乌苏市| 安泽县| 通道| 安龙县| 嵊州市| 龙口市| 高碑店市| 文水县| 富宁县| 淮滨县| 普陀区| 仪陇县| 广水市| 平山县| 康乐县| 黄骅市| 潢川县| 上林县| 贡嘎县| 香港| 永城市|