hibernate--一級和二級緩存(使用Ehcache)以及查詢緩存

有一下幾點需要理清纔行:

一級緩存是session緩存 session關閉就小時

二級緩存是sessionFactory級別的緩存 一個應用程序只有一個 多個線程共享  不要把經常修改的對象放到二級緩存中 二級緩存中放一些查詢的對象

1 首先是在hibernate,cfg.xml文件中進行配置:

添加下列配置項

                <property name="hibernate.cache.use_second_level_cache">true</property>
		<!-- 使用哪種緩存提供的類 哪種緩存 -->
		<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
		<!-- 使用查詢緩存-->
		<property name="hibernate.cache.use_query_cache">true</property>
		<!-- ehcache.xml的配置文件路徑 -->
		<property name="hibernate.cache.provider_configuration_file_resource_path">ehcache.xml</property>
在需要使用二級緩存的對象配置文件上:

	<class name="Student" table="t_stu">
		<!--  <cache usage="read-only" /> -->
		<id name="id">
			<generator class="native"></generator>
		</id>
		<!-- 注意:version 一定要加在ID後面 property前面 -->
		<version name="version" />
		<property name="name" />
		<property name="sex" />
		<many-to-one name="classroom" column="cid" fetch="join" />
	</class>
ehcache.xml中的配置:

    <defaultCache
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="true"
        />

<!-- 每一個獨立的cache可以單獨爲不同的對象進行設置 如果找不到就用默認的cache-->
    <cache name="com.itany.model.Student"
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="300"
        timeToLiveSeconds="600"
        overflowToDisk="true"
        />



2 N+1問題

 如果使用iterator返回列表 對於hibernate而言 
         它僅僅是查詢了id列表的SQL 在進行iterator迭代的時候 
         再會根據id一個個去數據庫查詢具體對象 因此發出多條SQL 這就是典型的N+1問題 避免它
         就是不使用iterator iterator存在的原因 
         使用的地方:
         因爲有可能需要查詢2次 第一次list全部查詢出來 存在二級緩存中 第二次 用Iterator數據庫查id,再根據ID從二級緩存的對象中查詢更快

            session = HibernateUtil.openSession();
            Iterator<Student> it = session.createQuery("from Student").setFirstResult(0).setMaxResults(12).iterate();
            while (it.hasNext())
            {
                Student stu = it.next();
                System.out.println(stu.getName());
            }
3  重要:二級緩存緩存的是對象  查詢緩存緩存的是ID 這是兩種不同的緩存  誰也不依賴誰   查詢緩存也是sessionFactory級別的緩存

     查詢緩存是針對HQL語句的緩存  查詢緩只緩存ID 而不會緩存對象
     1 但是使用查詢緩存最好打開二級緩存 因爲在查詢緩存中查到ID後可以直接去
     二級緩存中查找 不然的話又是N+1問題 多次根據查詢緩存中的ID去數據庫查詢
     2 若打開了二級緩存 但是沒有打開查詢緩存(HQL不一致、參數不一致、查詢緩存沒開、
     setCacheable(true)沒寫等原因)那麼還是會直接從數據庫中查詢一次、
     因爲需要藉助查詢緩存查ID再到二級緩存中查對象
     3 注意:load方式可以直接從二級緩存中查對象 不必藉助查詢緩存
     4如果取得只是某些屬性(而不是完整對象) 那麼不會進行二級緩存見test04
     5 查詢緩存 工作順序:如果正常開啓了查詢緩存(HQL完全一致 且set(True)),先去查詢緩存中查ID
          再根據查詢到的ID去二級緩存中查對象 若此時二級緩存打開了 那麼不會發SQL
         若二級緩存沒打開  那麼此時會再次根據查到的ID去數據庫中查詢

4 代碼中使用查詢緩存

  

 List<Object[]> ls = session.createQuery("select stu  from Student stu where name like ?")
                .setCacheable(true).setParameter(0, "%張%").list();//開啓查詢緩存  查詢緩存也是sessionFactory級別的緩存
5 此時關閉了二級緩存(存對象)
    打開了查詢緩存(存ID) 第二個人查詢緩存只查到ID 會根據ID 去二級緩存查對象  但是沒有  所以去數據庫查  發出大量SQL

6 查詢緩存很少使用 因爲很少HQL完全一樣
          因爲只要HQL語句不一樣 就不會開啓查詢緩存 
          只有兩個完全一樣的 並且參數都一樣的 才能使用查詢緩存

public class TestSecondCache
{
    /*
     * 重要:二級緩存緩存的是對象  查詢緩存緩存的是ID 這是兩種不同的緩存
     * 誰也不依賴誰  
     * 1 但是使用查詢緩存最好打開二級緩存 因爲在查詢緩存中查到ID後可以直接去
     * 二級緩存中查找 不然的話又是N+1問題 多次根據查詢緩存中的ID去數據庫查詢
     * 2 若打開了二級緩存 但是沒有打開查詢緩存(HQL不一致、參數不一致、查詢緩存沒開、
     * setCacheable(true)沒寫等原因)那麼還是會直接從數據庫中查詢一次、
     * 因爲需要藉助查詢緩存查ID再到二級緩存中查對象
     * 3 注意:load方式可以直接從二級緩存中查對象 不必藉助查詢緩存
     * 
     * 如果取得只是某些屬性(而不是完整對象) 那麼不會進行二級緩存見test04
     */
    
    @Test
    public void test01()
    {
        /*
         * 不可以緩存 Ehcache是看SQL是否一樣
         */
        Session session = null;
        try
        {
            session = HibernateUtil.openSession();
            Student s=(Student)session.load(Student.class,1);
            System.out.println(s.getName());
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (null != session)
                HibernateUtil.closeSession(session);
        }
        Session session2 = null;
        /*
         * 此時session已經關閉 但再次查詢 仍然可以不發SQL  二級緩存起作用了
         */
        try
        {
            session2 = HibernateUtil.openSession();
            Student s2=(Student)session2.load(Student.class,1);//這種寫法 根據ID去二級緩存中查詢
            System.out.println(s2.getName());
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (null != session2)
                HibernateUtil.closeSession(session2);
        }
    }
    @Test
    public void test02()
    {
        /*
         * 
         */
        Session session = null;
        try
        {
            session = HibernateUtil.openSession();
            Student s=(Student)session.load(Student.class,1);
            System.out.println(s.getName());
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (null != session)
                HibernateUtil.closeSession(session);
        }
        Session session2 = null;
        Transaction trans=null;
        /*
         * 會報錯 因爲二級緩存 在Student上設置的是 read-only 因此不能寫入
         * 最好不要設置 read write因爲hibernate要頻繁的加鎖 解鎖 影響效率 還不如不用二級緩存
         */
        try
        {
            session2 = HibernateUtil.openSession();
            trans=session2.beginTransaction();
            Student s2=(Student)session2.load(Student.class,1);
            s2.setName("zhangasnsss");
            System.out.println(s2.getName());
            trans.commit();
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (null != session2)
                HibernateUtil.closeSession(session2);
        }
    }
    @Test
    public void test03()
    {
        /*
         * 
         */
        Session session = null;
        Transaction trans = null;
        try
        {
            session = HibernateUtil.openSession();
            List<Student> ls = session.createQuery("from Student").list();
            Iterator<Student> it = ls.iterator();
            while (it.hasNext())
            {
                Student stu = it.next();
                System.out.println(stu.getName());
            }
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (null != session)
                HibernateUtil.closeSession(session);
        }
        
        try
        {
            session = HibernateUtil.openSession();
            Student s=(Student)session.load(Student.class,1);
            System.out.println(s.getName());
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (null != session)
                HibernateUtil.closeSession(session);
        }
    }
    @Test
    public void test04()
    {
        /*
         * 
         */
        Session session = null;
        Transaction trans = null;
        try
        {
            session = HibernateUtil.openSession();
            List<Object[]> ls = session.createQuery("select stu.id,stu.name from Student stu").list();
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (null != session)
                HibernateUtil.closeSession(session);
        }
        /*
         * 以上代碼僅僅去了id和name 而二級緩存是緩存對象的 所以上一段代碼不會使用二級緩存
         * 此時就會發出相應的SQL
         */
        try
        {
            session = HibernateUtil.openSession();
            Student s=(Student)session.load(Student.class,1);
            System.out.println(s.getName());
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (null != session)
                HibernateUtil.closeSession(session);
        }
    }
    @Test
    public void test05()
    {
        /*
         * iterator 作用就是配合二級緩存使用 最好
         */
        Session session = null;
        Transaction trans = null;
        try
        {   //一條SQL
            session = HibernateUtil.openSession();
            List<Object[]> ls = session.createQuery("select stu  from Student stu").list();
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (null != session)
                HibernateUtil.closeSession(session);
        }
        /* 1條SQL
         * 由於學生的對象經過上面查詢對象之後 已經存在於二級緩存之中
         * 此時正好直接使用iterator  首先會查詢通過1條取ID的語句  然後再獲取對象時候去二級緩存中查詢
         * 有的話 直接用 不會產生N+1問題
         * 
         *    若沒二級緩存 我們直接使用iterator會首先產生一條查ID的語句
         *    再在獲取對象的時候再去數據庫中取 那麼這是就會產生N+1問題
         */
        try
        {
            session = HibernateUtil.openSession();
            Iterator<Student> it = session.createQuery("from Student").iterate();
            while (it.hasNext())
            {
                Student stu = it.next();
                System.out.println(stu.getName());
            }
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (null != session)
                HibernateUtil.closeSession(session);
        }
    }
    @Test
    public void test06()
    {
        /*
         * 與上面一個幾乎一樣 區別在於iterator換成list() 
         * 雖然第一次已經二級緩存了  但是不能決定我再去不去數據庫中查詢
         * 
         * 重要 :如果希望第二次不發SQL 那就要使用查詢緩存
         * 查詢緩存是針對HQL語句的緩存  查詢緩只緩存ID 而不會緩存對象
         */
        Session session = null;
        
        Transaction trans = null;
        try
        {   //一條SQL
            session = HibernateUtil.openSession();
            List<Object[]> ls = session.createQuery("select stu  from Student stu").list();
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (null != session)
                HibernateUtil.closeSession(session);
        }
        /* 1條SQL  和上一條一模一樣
         * 
         */
        try
        {
            session = HibernateUtil.openSession();
            List<Student> ls = session.createQuery("select stu  from Student stu").list();
            Iterator<Student> it=ls.iterator();
            while (it.hasNext())
            {
                Student stu = it.next();
                System.out.println(stu.getName());
            }
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (null != session)
                HibernateUtil.closeSession(session);
        }
    }
    @Test
    public void test07()
    {
        /*
         * 設置了查詢緩存爲true
         */
        Session session = null;
        Transaction trans = null;
        try
        {   //一條SQL
            session = HibernateUtil.openSession();
            List<Object[]> ls = session.createQuery("select stu  from Student stu")
                .setCacheable(true).list();//開啓查詢緩存  查詢緩存也是sessionFactory級別的緩存
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (null != session)
                HibernateUtil.closeSession(session);
        }
        /*0條SQL 
         * 
         */
        try
        {
            session = HibernateUtil.openSession();
            List<Student> ls = session.createQuery("select stu  from Student stu")
                .setCacheable(true).list();
            Iterator<Student> it=ls.iterator();
            while (it.hasNext())
            {
                Student stu = it.next();
                System.out.println(stu.getName());
            }
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (null != session)
                HibernateUtil.closeSession(session);
        }
    }
    @Test
    public void test08()
    {
        /*
         * 此時無法使用查詢緩存 查詢緩存很少使用 因爲很少HQL完全一樣
         * 因爲只要HQL語句不一樣 就不會開啓查詢緩存 
         * 只有兩個完全一樣的 並且參數都一樣的 才能使用查詢緩存
         */
        Session session = null;
        Transaction trans = null;
        try
        {   //一條SQL
            session = HibernateUtil.openSession();
            List<Object[]> ls = session.createQuery("select stu  from Student stu where name like ?")
                .setCacheable(true).setParameter(0, "%張%").list();//開啓查詢緩存  查詢緩存也是sessionFactory級別的緩存
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (null != session)
                HibernateUtil.closeSession(session);
        }
        /*
         * 
         */
        try
        {
          //一條SQL
            session = HibernateUtil.openSession();
            List<Student> ls = session.createQuery("select stu  from Student stu where name like ?")
                .setCacheable(true).setParameter(0, "%王%").list();
            Iterator<Student> it=ls.iterator();
            while (it.hasNext())
            {
                Student stu = it.next();
                System.out.println(stu.getName());
            }
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (null != session)
                HibernateUtil.closeSession(session);
        }
    }
    @Test
    public void test09()
    {
        /*
         * 此時關閉了二級緩存(存對象)
         * 打開了查詢緩存(存ID)
         * !!!工作順序:如果正常開啓了查詢緩存(HQL完全一致 且set(True)),先去查詢緩存中查ID
         * 再根據查詢到的ID去二級緩存中查對象 若此時二級緩存打開了 那麼不會發SQL
         * 若二級緩存沒打開  那麼此時會再次根據查到的ID去數據庫中查詢
         */
        Session session = null;
        Transaction trans = null;
        try
        {   //一條SQL
            session = HibernateUtil.openSession();
            List<Object[]> ls = session.createQuery("select stu  from Student stu where name like ?")
                .setCacheable(true).setParameter(0, "%張%").list();//開啓查詢緩存  查詢緩存也是sessionFactory級別的緩存
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (null != session)
                HibernateUtil.closeSession(session);
        }
        /*
         * 
         */
        try
        {
          //此時關閉了student的二級緩存 打開了查詢緩存 發出大量SQL
            session = HibernateUtil.openSession();
            List<Student> ls = session.createQuery("select stu  from Student stu where name like ?")
                .setCacheable(true).setParameter(0, "%張%").list();
            Iterator<Student> it=ls.iterator();
            while (it.hasNext())
            {
                Student stu = it.next();
                System.out.println(stu.getName());
            }
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (null != session)
                HibernateUtil.closeSession(session);
        }
    }
    @Test
    public void test10()
    {
        /*
         * 此時打開了二級緩存(存對象)
         * 打開了查詢緩存(存ID)
         * !!!!工作順序:如果正常開啓了查詢緩存,先去查詢緩存中查ID
         * 再根據查詢到的ID去二級緩存中查對象 若此時二級緩存打開了 那麼不會發SQL
         * 若二級緩存沒打開  那麼此時會再次根據查到的ID去數據庫中查詢
         */
        Session session = null;
        Transaction trans = null;
        try
        {   //一條SQL
            session = HibernateUtil.openSession();
            List<Object[]> ls = session.createQuery("select stu  from Student stu where name like ?")
                .setCacheable(true).setParameter(0, "%張%").list();//開啓查詢緩存  查詢緩存也是sessionFactory級別的緩存
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (null != session)
                HibernateUtil.closeSession(session);
        }
        /*
         * 
         */
        try
        {
          //此時打開了student的二級緩存 打開了查詢緩存 不發SQL
            session = HibernateUtil.openSession();
            List<Student> ls = session.createQuery("select stu  from Student stu where name like ?")
                .setCacheable(true).setParameter(0, "%張%").list();
            Iterator<Student> it=ls.iterator();
            while (it.hasNext())
            {
                Student stu = it.next();
                System.out.println(stu.getName());
            }
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (null != session)
                HibernateUtil.closeSession(session);
        }
    }
    @Test
    public void test11()
    {
        /*
         * 此時打開了二級緩存(存對象)
         * 打開了查詢緩存(存ID)
         * !!!!工作順序:如果正常開啓了查詢緩存,先去查詢緩存中查ID
         * 再根據查詢到的ID去二級緩存中查對象 若此時二級緩存打開了 那麼不會發SQL
         * 若二級緩存沒打開  那麼此時會再次根據查到的ID去數據庫中查詢
         */
        Session session = null;
        Transaction trans = null;
        try
        {   //一條SQL
            session = HibernateUtil.openSession();
            List<Object[]> ls = session.createQuery("select stu  from Student stu where name like ?")
                .setCacheable(true).setParameter(0, "%張%").list();//開啓查詢緩存  查詢緩存也是sessionFactory級別的緩存
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (null != session)
                HibernateUtil.closeSession(session);
        }
        /*
         * 
         */
        try
        {
          //此時雖然打開了student的二級緩存 但是不能使用查詢緩存(HQL不一致)導致沒法通過查詢緩存查ID去
           //二級緩存中查對象
          //所以仍然發出一條SQL 從數據庫中查詢
            session = HibernateUtil.openSession();
            List<Student> ls = session.createQuery("select stu  from Student stu where name like ?")
                .setCacheable(true).setParameter(0, "%張三%").list();
            Iterator<Student> it=ls.iterator();
            while (it.hasNext())
            {
                Student stu = it.next();
                System.out.println(stu.getName());
            }
        }
        catch (HibernateException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (null != session)
                HibernateUtil.closeSession(session);
        }
    }
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章