轉載自http://blog.csdn.net/maoyeqiu/article/details/50209893
前兩天總結了一下二級緩存和查詢緩存的關系,但是又有一個新的問題,就是查詢緩存緩存到二級緩存的數據,在第三次(第一次緩存中沒有數據,查詢數據庫將對應的ID值存入到二級緩存中去,第二次如果是同一個Session那么將會把數據一級緩存中的數據返回,如果不是同一個Session而是同一個sessionfactory,那么將會把二級緩存中的數據返回,同時將數據放入到一級緩存中去)獲取的時候,不使用查詢緩存的方法,而是直接使用一級緩存的方法,能不能將緩存的數據獲取到呢,我們現在驗證一下。
首先開啟一級緩存(默認)、二級緩存和查詢緩存
- <?xml version="1.0" encoding="utf-8"?>
- <!DOCTYPE hibernate-configuration PUBLIC
- "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
- "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
- <hibernate-configuration>
- <session-factory>
- <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
- <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernatedemo</property>
- <property name="hibernate.connection.password">root</property>
- <property name="hibernate.connection.username">root</property>
- <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
- <property name="show_sql">true</property>
- <property name="hbm2ddl.auto">create</property>
- <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
- <property name="hibernate.cache.use_query_cache">true</property>
- <!-- 開啟二級緩存,其實hibernate默認就是開啟的,這里顯示的指定一下 -->
- <property name="hibernate.cache.use_second_level_cache">true</property>
- <property name="hibernate.generate_statistics">true</property>
- <mapping class="hibernate.test.dto.DepartmentEntity"></mapping>
- </session-factory>
- </hibernate-configuration>
用到的緩存類
- package hibernate.test.dto;
- @Entity
- @Table(name = "DEPARTMENT", uniqueConstraints = {
- @UniqueConstraint(columnNames = "ID"),
- @UniqueConstraint(columnNames = "NAME") })
- @Cache(usage=CacheConcurrencyStrategy.READ_ONLY, region="department")
- public class DepartmentEntity implements Serializable {
- private static final long serialVersionUID = 1L;
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- @Column(name = "ID", unique = true, nullable = false)
- private Integer id;
- @Column(name = "NAME", unique = true, nullable = false, length = 100)
- private String name;
- public Integer getId() {
- return id;
- }
- public void setId(Integer id) {
- this.id = id;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
啟動查詢緩存將返回的實體存入到二級緩存中去
- //此方法向數據庫中存入三條數據
- storeData();
- Session session = HibernateUtil.getSessionFactory().openSession();
- session.beginTransaction();
- //開啟查詢緩存, query.setCacheable(true);開啟查詢緩存
- Query query = session.createQuery("select s from DepartmentEntity s");
- query.setCacheable(true);
- List<DepartmentEntity> names = query.list();
- for (Iterator<DepartmentEntity> it = names.iterator(); it.hasNext();) {
- String name = it.next().getName();
- System.out.println(name);
- }
- System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCachePutCount());
執行的結果:
hibernate: select department0_.ID as ID0_, department0_.NAME as NAME0_ from DEPARTMENT department0_
Human Resource
Humanne
Jhon
3
我們可以看到,產生了一條查詢語句,將三個實體的名字輸出,放入二級緩存的數量是3,都是沒有問題的。這里比較容易犯的一個錯誤是查詢的結果並不是實體,而是名字或者是其他屬性,比如sql我們這么寫:select s.name from DepartmentEntity s。存入二級緩存的數據是0,也就是沒有存入到二級緩存中去。
再次調用上述方法:
- //開啟查詢緩存, query.setCacheable(true);開啟查詢緩存
- Query query = session.createQuery("select s from DepartmentEntity s");
- query.setCacheable(true);
- List<DepartmentEntity> names = query.list();
- for (Iterator<DepartmentEntity> it = names.iterator(); it.hasNext();) {
- String name = it.next().getName();
- System.out.println(name);
- }
- System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCachePutCount());
- //同一個Session中再次查詢,不會發出SQL語句到數據庫
- query = session.createQuery("select s from DepartmentEntity s");
- query.setCacheable(true);
- names = query.list();
- for (Iterator<DepartmentEntity> it = names.iterator(); it.hasNext();) {
- String name = it.next().getName();
- System.out.println(name);
- }
- System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCachePutCount());
- System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCacheHitCount());
執行的結果:
Hibernate: select department0_.ID as ID0_, department0_.NAME as NAME0_ from DEPARTMENT department0_
Human Resource
Humanne
Jhon
3
Human Resource
Humanne
Jhon
3
0
我們可以看到只產生了一條數據庫查詢語句,第二次直接從緩存中獲取(注意,第二次調用的時候依然要寫quey.setCacheable(true); ,我們模擬的是同一個方法的多次調用,不然的會產生數據庫查詢),那么問題來了,這個緩存指的是一級緩存還是二級緩存呢,我們看執行的結果二級緩存的命中是0,也就是說這里並沒有用到二級緩存而是直接使用了一級緩存,前提是在同一個Session的情況下,在同一個session下執行的結果都會首先緩存到一級緩存中去,那么我們開一個新的Session會有什么樣的不同結果呢
- //開啟查詢緩存, query.setCacheable(true);開啟查詢緩存
- Query query = session.createQuery("select s from DepartmentEntity s");
- query.setCacheable(true);
- List<DepartmentEntity> names = query.list();
- for (Iterator<DepartmentEntity> it = names.iterator(); it.hasNext();) {
- String name = it.next().getName();
- System.out.println(name);
- }
- System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCachePutCount());
- //同一個Session中再次查詢,不會發出SQL語句到數據庫
- query = session.createQuery("select s from DepartmentEntity s");
- quey.setCacheable(true);
- names = query.list();
- for (Iterator<DepartmentEntity> it = names.iterator(); it.hasNext();) {
- String name = it.next().getName();
- System.out.println(name);
- }
- System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCachePutCount());
- System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCacheHitCount());
- Session anotherSession = HibernateUtil.getSessionFactory().openSession();
- anotherSession.beginTransaction();
- query = anotherSession.createQuery("select s from DepartmentEntity s");
- query.setCacheable(true);
- names = query.list();
- for (Iterator<DepartmentEntity> it = names.iterator(); it.hasNext();) {
- String name = it.next().getName();
- System.out.println(name);
- }
- System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCachePutCount());
- System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCacheHitCount());
執行結果:
- Hibernate: select department0_.ID as ID0_, department0_.NAME as NAME0_ from DEPARTMENT department0_
- Human Resource
- Humanne
- Jhon
- 3
- Human Resource
- Humanne
- Jhon
- 3
- 0
- Human Resource
- Humanne
- Jhon
- 3
- 3
我們看到運行的結果,三次執行都只是放入二級緩存的實例3個,也就是說二級緩存中只有三個實例。由於二級緩存是sessionfactory級別的當開啟查詢緩存將數據放入的二級緩存的時候是不受開了幾個session影響的,所以盡管我們上邊開啟了2個session但是依舊是在二級緩存中有3個實體。這里還有一個問題是查詢緩存的key值是如何定義的呢,導致了開啟了3次查詢緩存而只存入3條數據,如果key值不同的話,那么肯定是會存入9條數據,關於這個問題大家可以參考,這里。由於二級緩存是sessionfactory級別的,因此會直接將二級緩存中的數據取出,並存入到一級緩存中去。
我們在sql中只是查詢實體的名字,我們來看一下查詢緩存是如何緩存的
- //開啟查詢緩存, query.setCacheable(true);開啟查詢緩存
- ,Query query = session.createQuery("select s.name from DepartmentEntity s");
- query.setCacheable(true);
- System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCachePutCount());
- System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCacheHitCount());
- //同一個Session中再次查詢,不會發出SQL語句到數據庫
- query = session.createQuery("select s.name from DepartmentEntity s");
- query.setCacheable(true);
- System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCachePutCount());
- System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCacheHitCount());
- Session anotherSession = HibernateUtil.getSessionFactory().openSession();
- anotherSession.beginTransaction();
- query = anotherSession.createQuery("select s.name from DepartmentEntity s");
- query.setCacheable(true);
- System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCachePutCount());
- System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCacheHitCount());
執行結果:
Hibernate: select department0_.NAME as col_0_0_ from DEPARTMENT department0_
0
0
0
0
0
0
從結果上我們可以看到只有一個數據庫查詢,然后是六個0,前兩個0是沒有將數據存入到二級緩存中去,中間兩個0並且沒有sql數據庫查詢說明,數據從緩存中獲取,而且是一級緩存中的數據,后兩個0我們重新開啟了一個session,同樣沒有數據庫查詢,也就說是調用了二級緩存中的數據,也就說明了查詢緩存也是sessionfactory級別的。
我們現在來驗證一下我們剛才提出的問題,直接用load獲取數據
- DepartmentEntity department = (DepartmentEntity) session.load(DepartmentEntity.class, new Integer(1));
- System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCacheHitCount());
執行結果:
0
從結果來看,沒有產生數據庫查詢二級緩存的命中是0,也就是說數據是從一級緩存中獲取的,這就驗證了我們一開始提到的答案。
總結:
1、一級緩存是session級別的,二級緩存和查詢緩存都是sessionfactory級別的,查詢緩存和二級緩存是一起來使用的
2、任何sql執行都會存入到同一個session的一級緩存中去
3、同時開啟查詢緩存和二級緩存,可以在不同session間共享緩存的結果
4、二級緩存緩存的是實體,不是屬性
5、查詢緩存的結果如果只是屬性,那么查詢緩存中存儲的是id和屬性的值,如果是實體的集合,那么查詢緩存存儲的只是實體的id,對應的實體會存儲到二級緩存中去。
6、不同session間返回數據的順序是,二級緩存先將數據返回,然后將數據存入本session的一級緩存中去,以便下次調用時的使用