1. 什么是緩存?
數據庫的緩存指的是應用程序和物理數據源之間的數據。即把物理數據源的數據復制到緩存。有了緩存,可以降低應用程序對物理數據源的訪問頻率,從而提高效率。緩存的介質一般是內存,也可以是硬盤。
Hibernate的緩存有三種類型:一級緩存、二級緩存和查詢緩存。
2. 一級緩存
一級緩存即Session緩存,由Session自動進行管理,不需要程序進行干預。一級緩存根據對象的ID進行加載和緩存。如下面的代碼:
1
2
3
4
5
6
7
8
9
10
11
12
|
@Override public void testCache() { // TODO Auto-generated method stub Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Course c = (Course) session.get(Course. class , 1 ); System.out.println( "Name:" + c.getName()); c = (Course) session.get(Course. class , 1 ); System.out.println( "Name:" + c.getName()); tx.commit(); session.close(); } |
運行結果:
1
2
3
4
5
6
7
8
9
10
11
|
Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Name:計算機原理 Name:計算機原理 |
第1次查詢時生成了SQL語句,并將查詢出來的對象放在一級緩存里面,第2次查詢時,在一級緩存里面直接找到了這個對象,就不需要再次生成SQL語句了。
再看一個例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@Override public void testCache() { // TODO Auto-generated method stub Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Course c = (Course) session.get(Course. class , 1 ); System.out.println( "Name:" + c.getName()); tx.commit(); session.close(); session = sessionFactory.openSession(); tx = session.beginTransaction(); c = (Course) session.get(Course. class , 1 ); System.out.println( "Name:" + c.getName()); tx.commit(); session.close(); } |
由于一級緩存是Session級別的緩存,所以Session關閉以后,一級緩存也就不存在了,第2次查詢也要生成SQL語句:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
Hibernate: select course0_.ID as ID0_0_, course0_. NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Name :計算機原理 Hibernate: select course0_.ID as ID0_0_, course0_. NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Name :計算機原理 |
3. 二級緩存
二級緩存即SessionFactory緩存,和一級緩存類似,也是根據對象的ID進行加載和緩存,區別就在于一級緩存只在Session內有效,而二級緩存在SessionFactory內有效。在訪問某個ID的對象時,先到一級緩存里面去找,如果沒有找到就到二級緩存里面去找。二級緩存包括EHCache,OSCache,SwarmCache和JBossCache等。這里以EHCache作為例子。
二級緩存需要程序進行管理。首先,配置Maven下載相關的Jar,在pom文件里面添加:
1
2
3
4
5
6
7
8
9
10
|
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-ehcache</artifactId> <version> 4.1 . 0 .Final</version> </dependency> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> <version> 2.8 . 3 </version> </dependency> |
創建EHCache配置文件ehcache.xml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
< ehcache > < diskStore path = "E:\Eclipse\MyWorkspace\Cache" /> < defaultCache maxElementsInMemory = "10000" eternal = "true" timeToIdleSeconds = "120" timeToLiveSeconds = "120" overflowToDisk = "true" /> < cache name = "com.hzhi.course.entity.Course" maxElementsInMemory = "10000" eternal = "true" timeToIdleSeconds = "300" timeToLiveSeconds = "600" overflowToDisk = "true" /> </ ehcache > |
defaultCache是默認的設置,下面一個cache指明了對哪一個類進行二級緩存。里面設置了最大緩存的對象數量,是否永久有效、最大空閑秒數、最大生存秒數、內存滿時是否寫到硬盤,寫到硬盤的路徑等等。
修改需要緩存的類的hbm文件:
1
2
3
4
|
< class name= "com.hzhi.course.entity.Course" table= "clas" > <cache usage= "read-only" /> ...... </ class > |
usage設置了并發訪問策略,一般設置成read-only。
修改applicationContext.xml中的SessionFactory的配置,增加二級緩存的一些屬性:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
<!-- SessionFactory --> < bean id = "sessionFactory" class = "org.springframework.orm.hibernate4.LocalSessionFactoryBean" > < property name = "dataSource" > < ref local = "dataSource" /> </ property > <!-- 配置Hibernate的屬性 --> < property name = "hibernateProperties" > < props > < prop key = "hibernate.dialect" >org.hibernate.dialect.MySQLDialect</ prop > < prop key = "hibernate.show_sql" >true</ prop > < prop key = "hibernate.format_sql" >true</ prop > < prop key = "hibernate.connection.isolation" >8</ prop > <!-- 二級緩存 --> < prop key = "hibernate.cache.use_second_level_cache" >false</ prop > < prop key = "hibernate.cache.region.factory_class" >org.hibernate.cache.ehcache.EhCacheRegionFactory</ prop > < prop key = "hibernate.cache.provider_configuration_file_resource_path" >WEB-INF/ehcache.xml</ prop > </ props > </ property > ...... </ bean > |
運行下面的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@Override public void testCache() { // TODO Auto-generated method stub Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Course c = (Course) session.get(Course. class , 1 ); System.out.println( "Name:" + c.getName()); tx.commit(); session.close(); session = sessionFactory.openSession(); tx = session.beginTransaction(); c = (Course) session.get(Course. class , 1 ); System.out.println( "Name:" + c.getName()); tx.commit(); session.close(); } |
結果:
1
2
3
4
5
6
7
8
9
10
11
|
Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Name:計算機原理 Name:計算機原理 |
雖然關閉了Session,但是二級緩存仍然存在,所以只生成了一次SQL語句。
下面的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
@Override public void testCache() { // TODO Auto-generated method stub Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Query query = session.createQuery( "from Course" ); Iterator iter = query.iterate(); while (iter.hasNext()){ System.out.println(((Course)iter.next()).getName()); } tx.commit(); session.close(); session = sessionFactory.openSession(); tx = session.beginTransaction(); query = session.createQuery( "from Course" ); iter = query.iterate(); while (iter.hasNext()){ System.out.println(((Course)iter.next()).getName()); } tx.commit(); session.close(); } |
結果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
|
Hibernate: select course0_.ID as col_0_0_ from clas course0_ Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? 計算機原理 Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? 計算機網絡 Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? 數據庫原理 Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? C語言 Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? 大學英語A Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Java Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Linux Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? 高等數學 Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? 語文 Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? 大學物理 Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? 軟件工程 Hibernate: select course0_.ID as col_0_0_ from clas course0_ 計算機原理 計算機網絡 數據庫原理 C語言 大學英語A Java Linux 高等數學 語文 大學物理 軟件工程 |
當使用Query的list()方法時,只生成一次SQL語句查詢出所有的對象,使用iterate()方法時,會先得到所有對象的ID,然后根據每個ID生成一次SQL語句查詢。第二個Session里面使用的也是iterate()方法,首先生成一次SQL語句,得到ID,然后根據ID查找對象,由于開啟了二級緩存,在二級緩存里面找到了對象,所以就直接輸出了,并沒有再根據每個ID生成SQL語句。
不論是一級緩存還是二級緩存,都只能緩存對象,不能緩存屬性的值。下面的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
@Override public void testCache() { // TODO Auto-generated method stub Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Query query = session.createQuery( "select c.name from Course c" ); List<String> names = query.list(); for (Iterator iter = names.iterator(); iter.hasNext();){ String name = (String) iter.next(); System.out.println(name); } System.out.println( "----------" ); query = session.createQuery( "select c.name from Course c" ); names = query.list(); for (Iterator iter = names.iterator(); iter.hasNext();){ String name = (String) iter.next(); System.out.println(name); } System.out.println( "----------" ); tx.commit(); session.close(); } |
運行結果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
Hibernate: select course0_.NAME as col_0_0_ from clas course0_ 計算機原理 計算機網絡 數據庫原理 C語言 大學英語A Java Linux 高等數學 語文 大學物理 軟件工程 ---------- Hibernate: select course0_.NAME as col_0_0_ from clas course0_ 計算機原理 計算機網絡 數據庫原理 C語言 大學英語A Java Linux 高等數學 語文 大學物理 軟件工程 ---------- |
雖然開啟了二級緩存,但是查詢的結果不是對象,是屬性,所以并沒有緩存,第2次查詢仍然生成了查詢語句。要解決這個問題,就需要查詢緩存。
3. 查詢緩存
在配置了二級緩存的基礎上,可以設置查詢緩存,在SessionFactory的設置里面加上一行:
<prop key="hibernate.cache.use_query_cache">true</prop>
即打開了查詢緩存。查詢緩存也是SessionFactory級別的緩存,在整個SessionFactory里面都是有效的。
關閉二級緩存,運行下面的例子,在Query后面添加setCacheable(true)打開查詢緩存:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
@Override public void testCache() { // TODO Auto-generated method stub Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Query query = session.createQuery( "select c.name from Course c" ); query.setCacheable( true ); List<String> names = query.list(); for (Iterator iter = names.iterator(); iter.hasNext();){ String name = (String) iter.next(); System.out.println(name); } System.out.println( "----------" ); query = session.createQuery( "select c.name from Course c" ); query.setCacheable( true ); names = query.list(); for (Iterator iter = names.iterator(); iter.hasNext();){ String name = (String) iter.next(); System.out.println(name); } System.out.println( "----------" ); tx.commit(); session.close(); } |
結果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
Hibernate: select course0_.NAME as col_0_0_ from clas course0_ 計算機原理 計算機網絡 數據庫原理 C語言 大學英語A Java Linux 高等數學 語文 大學物理 軟件工程 ---------- 計算機原理 計算機網絡 數據庫原理 C語言 大學英語A Java Linux 高等數學 語文 大學物理 軟件工程 ---------- |
由于兩次查詢的HQL語句是一致的,所以只生成一次SQL語句。但是如果把第二次查詢改一下:
1
2
3
4
5
6
7
8
9
|
System.out.println( "----------" ); query = session.createQuery( "select c.name from Course c where c.id > 5" ); query.setCacheable( true ); names = query.list(); for (Iterator iter = names.iterator(); iter.hasNext();){ String name = (String) iter.next(); System.out.println(name); } System.out.println( "----------" ); |
結果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
Hibernate: select course0_.NAME as col_0_0_ from clas course0_ 計算機原理 計算機網絡 數據庫原理 C語言 大學英語A Java Linux 高等數學 語文 大學物理 軟件工程 ---------- Hibernate: select course0_.NAME as col_0_0_ from clas course0_ where course0_.ID>5 大學英語A Java Linux 高等數學 語文 大學物理 軟件工程 ---------- |
由于HQL語句變了,所以第二次也生成了SQL語句。
查詢緩存可以緩存屬性,也可以緩存對象,但是當緩存對象時,只緩存對象的ID,不會緩存整個對象。下面的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
@Override public void testCache() { // TODO Auto-generated method stub Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Query query = session.createQuery( "from Course" ); query.setCacheable( true ); List<Course> list = query.list(); for ( int i= 0 ; i<list.size(); i++){ System.out.println(list.get(i).getName()); } System.out.println( "----------" ); tx.commit(); session.close(); session = sessionFactory.openSession(); tx = session.beginTransaction(); query = session.createQuery( "from Course" ); query.setCacheable( true ); list = query.list(); for ( int i= 0 ; i<list.size(); i++){ System.out.println(list.get(i).getName()); } System.out.println( "----------" ); tx.commit(); session.close(); } |
結果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
Hibernate: select course0_.ID as ID0_, course0_.NAME as NAME0_, course0_.COMMENT as COMMENT0_ from clas course0_ 計算機原理 計算機網絡 數據庫原理 C語言 大學英語A Java Linux 高等數學 語文 大學物理 軟件工程 ---------- Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? 計算機原理 計算機網絡 數據庫原理 C語言 大學英語A Java Linux 高等數學 語文 大學物理 軟件工程 ---------- |
由于開了查詢緩存,沒有開二級緩存,雖然使用的是list()方法一次查詢出了所有的對象,但是查詢緩存只緩存了對象ID,沒有緩存整個對象。所以在第2個Session里面"from Course"這個HQL由于和前面的相同,并沒有生成SQL語句,但是由于沒有開二級緩存,沒有緩存整個對象,只能根據每個ID去生成一次SQL語句。雖然兩次用的都是list()方法,但是第一次是生成SQL語句去一次查詢出所有的對象,而第二次是根據查詢緩存里面的ID一個一個的生成SQL語句。
如果同時打開查詢緩存和二級緩存,第2個Session里面就不用再根據ID去生成SQL語句了:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
Hibernate: select course0_.ID as ID0_, course0_. NAME as NAME0_, course0_.COMMENT as COMMENT0_ from clas course0_ 計算機原理 計算機網絡 數據庫原理 C語言 大學英語A Java Linux 高等數學 語文 大學物理 軟件工程 ---------- 計算機原理 計算機網絡 數據庫原理 C語言 大學英語A Java Linux 高等數學 語文 大學物理 軟件工程 ---------- |
以上就是本文的全部內容,希望本文的內容對大家的學習或者工作能帶來一定的幫助,同時也希望多多支持服務器之家!
原文鏈接:http://www.cnblogs.com/mstk/p/6363351.html