有一下幾點需要理清纔行:
一級緩存是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);
}
}
}