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>
ehcache.xml中的配置:<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>
<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);
}
}
}