spring-data-jpa使用快取的注意事項

gongxufan發表於2019-03-03

前言

採用hibernate的JPA實現,對於簡單的查詢十分方便。而對於複雜查詢我們也可以寫SQL來進行復雜的多表連線查詢。很多人不喜歡hibernate其實更多的是對其機制的掌握不深,如果認真研究其實現原始碼,其實是一個很快樂的學習過程。各種設計正規化的運用也是精彩絕倫。

這裡主要說下快取的配置。既然是hibernate,其快取機制離不開這三種:session級別的快取、sessionFactory級別的二級快取以及查詢快取和帶有集合的快取。對於jpa的使用,個人建議不要使用關係對映。這樣會導致各種關聯查詢,涉及到延遲載入等機制,會消耗額外的cglib的大量代理工作。尤其是很多人還使用org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter來實現集合的頁面懶載入。這對http響應要求很高的系統簡直就是災難。

使用jpa要放棄hibernate的關係對映部分,保持開發的簡潔和靈活性。

先上測試程式碼

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
複製程式碼
package cn.com.egova.easyshare.test.service;  
  
import cn.com.egova.easyshare.common.dto.NavMenuDto;  
import cn.com.egova.easyshare.common.entity.Human;  
import cn.com.egova.easyshare.common.entity.NavMenu;  
import cn.com.egova.easyshare.server.service.IHumanService;  
import org.junit.Test;  
import org.junit.runner.RunWith;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.test.context.ContextConfiguration;  
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;  
  
import javax.persistence.EntityManager;  
import javax.persistence.EntityManagerFactory;  
import java.util.List;  
  
@RunWith(SpringJUnit4ClassRunner.class)  
@ContextConfiguration({"classpath:/applicationContext.xml"})  
public class HumanServiceTest {  
    @Autowired  
    private IHumanService humanService;  
  
    @Autowired  
    private EntityManagerFactory entityManagerFactory;  
  
    //一級快取:內建的session級別的快取,預設開啟  
    @Test  
    public void sessionCacheTest(){  
        EntityManager entityManager = entityManagerFactory.createEntityManager();  
  
        Human human = entityManager.find(Human.class,37);  
        Human human1 = entityManager.find(Human.class,37);  
        System.out.println(human == human1?"true":"false");  
        entityManager.close();  
  
        entityManager = entityManagerFactory.createEntityManager();  
  
        Human human3 = entityManager.find(Human.class,37);  
        System.out.println(human == human3?"true":"false");  
    }  
  
    //二級快取,同一個sessionFactory的不同session快取,預設關閉  
    public void sessionFactoryCacheTest(){  
        EntityManager entityManager = entityManagerFactory.createEntityManager();  
  
        NavMenu navMenu = entityManager.find(NavMenu.class,1);  
        entityManager.close();  
  
        entityManager = entityManagerFactory.createEntityManager();  
        NavMenu navMenu2 = entityManager.find(NavMenu.class,1);  
        entityManager.close();  
  
        System.out.println(navMenu == navMenu2?"true":"false");  
    }  
  
    //查詢快取,前兩種都是根據主鍵ID的快取策略.  
    @Test  
    public void queryCacheTest() throws Exception{  
        List<NavMenuDto> navMenuDtos = humanService.getMenuList(37);  
        List<NavMenuDto> navMenuDtos2 = humanService.getMenuList(37);  
    }  
  
}
複製程式碼

一級快取

也就是自建的session級別的快取,在一個session回話事物中根據主鍵查詢是隻會查詢一次資料庫的。

1
2
3
4
5
6
7
8
複製程式碼
EntityManager entityManager = entityManagerFactory.createEntityManager();  
Human human = entityManager.find(Human.class,37);  
Human human1 = entityManager.find(Human.class,37);  
System.out.println(human == human1?"true":"false");  
entityManager.close();  
entityManager = entityManagerFactory.createEntityManager();  
Human human3 = entityManager.find(Human.class,37);  
 System.out.println(human == human3?"true":"false");
複製程式碼

控制檯列印:

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
複製程式碼
Hibernate:   
    select  
        human0_.humanID as humanID1_11_0_,  
        human0_.activeFlag as activeFlag2_11_0_,  
        human0_.address as address3_11_0_,  
        human0_.createDate as createDate4_11_0_,  
        human0_.description as description5_11_0_,  
        human0_.displayOrder as displayOrder6_11_0_,  
        human0_.email as email7_11_0_,  
        human0_.humanCode as humanCode8_11_0_,  
        human0_.humanName as humanName9_11_0_,  
        human0_.humanPassword as humanPassword10_11_0_,  
        human0_.identifyType as identifyType11_11_0_,  
        human0_.orgId as orgId12_11_0_,  
        human0_.tel as tel13_11_0_,  
        human0_.unitID as unitID14_11_0_,  
        human0_.userid as userid15_11_0_,  
        human0_.userroles as userroles16_11_0_,  
        human0_.zipCode as zipCode17_11_0_   
    from  
        tcHuman human0_   
    where  
        human0_.humanID=?  
true  
2016-07-20 14:24:30 INFO main org.hibernate.engine.internal.StatisticalLoggingSessionEventListener - Session Metrics {
    399787 nanoseconds spent acquiring 1 JDBC connections;  
    0 nanoseconds spent releasing 0 JDBC connections;  
    114082560 nanoseconds spent preparing 1 JDBC statements;  
    2907307 nanoseconds spent executing 1 JDBC statements;  
    0 nanoseconds spent executing 0 JDBC batches;  
    0 nanoseconds spent performing 0 L2C puts;  
    0 nanoseconds spent performing 0 L2C hits;  
    0 nanoseconds spent performing 0 L2C misses;  
    0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);  
    0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)  
}  
Hibernate:   
    select  
        human0_.humanID as humanID1_11_0_,  
        human0_.activeFlag as activeFlag2_11_0_,  
        human0_.address as address3_11_0_,  
        human0_.createDate as createDate4_11_0_,  
        human0_.description as description5_11_0_,  
        human0_.displayOrder as displayOrder6_11_0_,  
        human0_.email as email7_11_0_,  
        human0_.humanCode as humanCode8_11_0_,  
        human0_.humanName as humanName9_11_0_,  
        human0_.humanPassword as humanPassword10_11_0_,  
        human0_.identifyType as identifyType11_11_0_,  
        human0_.orgId as orgId12_11_0_,  
        human0_.tel as tel13_11_0_,  
        human0_.unitID as unitID14_11_0_,  
        human0_.userid as userid15_11_0_,  
        human0_.userroles as userroles16_11_0_,  
        human0_.zipCode as zipCode17_11_0_   
    from  
        tcHuman human0_   
    where  
        human0_.humanID=?  
false
複製程式碼

執行entityManager.close();entityManager關閉後會重新執行一次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
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
複製程式碼
<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
       xmlns:context="http://www.springframework.org/schema/context"  
       xmlns:aop="http://www.springframework.org/schema/aop"  
       xmlns:tx="http://www.springframework.org/schema/tx"  
       xmlns:p="http://www.springframework.org/schema/p"  
       xmlns:cache="http://www.springframework.org/schema/cache"  
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"  
  
       xsi:schemaLocation="http://www.springframework.org/schema/beans  
         http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
          http://www.springframework.org/schema/context  
          http://www.springframework.org/schema/context/spring-context-3.1.xsd  
          http://www.springframework.org/schema/aop  
          http://www.springframework.org/schema/aop/spring-aop-3.1.xsd  
          http://www.springframework.org/schema/tx  
          http://www.springframework.org/schema/tx/spring-tx-3.1.xsd  
          http://www.springframework.org/schema/cache  
          http://www.springframework.org/schema/cache/spring-cache-3.1.xsd  
          http://www.springframework.org/schema/data/jpa  
          http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">  
  
    <context:annotation-config />  
  
    <context:component-scan base-package="cn.com.egova.easyshare"/>  
    <!--配置檔案讀取-->  
    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
        <property name="locations">  
            <list>  
                <value>classpath:config.properties</value>  
            </list>  
        </property>  
  
    </bean>  
    <bean id="springContextUtil" class="cn.com.egova.easyshare.common.utils.SpringContextUtil"></bean>  
    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">  
        <property name="driverClassName" value="${db.driverClassName}"></property>  
        <property name="url" value="${db.url}"></property>  
        <property name="username" value="${db.username}"></property>  
        <property name="password" value="${db.password}"></property>  
        <property name="maxTotal" value="${db.maxTotal}"></property>  
        <property name="maxIdle" value="${db.maxIdle}"></property>  
    </bean>  
  
    <bean id="entityManagerFactory"  
          class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">  
        <property name="dataSource" ref="dataSource" />  
        <property name="jpaVendorAdapter">  
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">  
                <property name="generateDdl" value="false" />  
                <property name="database" value="ORACLE" />  
            </bean>  
        </property>  
        <!-- 指定Entity實體類包路徑 -->  
        <property name="packagesToScan">  
            <value>cn.com.egova.easyshare.common.entity</value>  
        </property>  
        <!-- 指定JPA屬性;如Hibernate中指定是否顯示SQL的是否顯示、方言等 -->  
        <property name="jpaProperties">  
            <props>  
                <prop key="hibernate.dialect">org.hibernate.dialect.Oracle9iDialect</prop>  
                <prop key="hibernate.show_sql">true</prop>  
                <prop key="hibernate.format_sql">true</prop>  
                <prop key="hibernate.generate_statistics">true</prop>  
                <prop key="hibernate.hbm2ddl.auto">none</prop>  
                <!-- 配置二級快取 -->  
                <prop key="hibernate.cache.use_second_level_cache">true</prop>  
                <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>  
                <!-- 開啟查詢快取 -->  
                <prop key="hibernate.cache.use_query_cache">true</prop>  
                <prop key="hibernate.cache.provider_configuration">classpath:ehcache.xml</prop>  
            </props>  
        </property>  
    </bean>  
  
    <!-- 配置事務管理器 -->  
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">  
        <property name="entityManagerFactory" ref="entityManagerFactory" />  
    </bean>  
  
    <!-- 啟用 annotation事務-->  
    <tx:annotation-driven transaction-manager="transactionManager"/>  
      
      
    <!-- 採用JdbcTemplate配置 -->  
    <bean id="jdbcTemplate"  class="org.springframework.jdbc.core.JdbcTemplate">    
        <property name="dataSource" ref="dataSource" />    
    </bean>  
      
    <bean id="lobHandler"  class="org.springframework.jdbc.support.lob.OracleLobHandler">    
    </bean>  
      
    <!-- 通過aop配置事務 -->  
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
        <property name="dataSource" ref="dataSource"/>  
    </bean>  
      
  
    <aop:aspectj-autoproxy/>  
    <!-- 配置Spring Data JPA掃描目錄-->  
    <jpa:repositories base-package="cn.com.egova.easyshare.server.repository"/>  
</beans>
複製程式碼

然後在需要使用快取的entity配置好快取註解

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
複製程式碼
package cn.com.egova.easyshare.common.entity;  
  
import org.hibernate.annotations.Cache;  
import org.hibernate.annotations.CacheConcurrencyStrategy;  
  
import javax.persistence.*;  
  
/** 
 * 系統選單 
 * Created by gongxufan on 2016/3/13.
 */  
@Entity  
@Table(name = "TCSYSNAVMENU")  
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)  
@Cacheable(true)  
public class NavMenu {  
    @Id  
    @GeneratedValue(strategy = GenerationType.TABLE, generator = "tableKeyGenerator")  
    @TableGenerator(name = "tableKeyGenerator", table = "tcTableKeyGenerator",  
            pkColumnName = "pk_key", valueColumnName = "pk_value", pkColumnValue = "menuID",  
            initialValue = 1, allocationSize = 1)  
    private Integer menuID;  
    private Integer seniorMenuID;  
    private String menuName;  
    private String menuDescr;  
    private String menuIconClass;  
    private String hrefUrl;  
    private Integer displayOrder;  
  
    public Integer getMenuID() {  
        return menuID;  
    }  
  
    public void setMenuID(Integer menuID) {  
        this.menuID = menuID;  
    }  
  
    public Integer getSeniorMenuID() {  
        return seniorMenuID;  
    }  
  
    public void setSeniorMenuID(Integer seniorMenuID) {  
        this.seniorMenuID = seniorMenuID;  
    }  
  
    public String getMenuName() {  
        return menuName;  
    }  
  
    public void setMenuName(String menuName) {  
        this.menuName = menuName;  
    }  
  
    public String getMenuDescr() {  
        return menuDescr;  
    }  
  
    public void setMenuDescr(String menuDescr) {  
        this.menuDescr = menuDescr;  
    }  
  
    public String getMenuIconClass() {  
        return menuIconClass;  
    }  
  
    public void setMenuIconClass(String menuIconClass) {  
        this.menuIconClass = menuIconClass;  
    }  
  
    public String getHrefUrl() {  
        return hrefUrl;  
    }  
  
    public void setHrefUrl(String hrefUrl) {  
        this.hrefUrl = hrefUrl;  
    }  
  
    public Integer getDisplayOrder() {  
        return displayOrder;  
    }  
  
    public void setDisplayOrder(Integer displayOrder) {  
        this.displayOrder = displayOrder;  
    }  
}
複製程式碼

當然還要配置好ehcache.xml

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
複製程式碼
<?xml version="1.0" encoding="UTF-8"?>  
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"  
         updateCheck="false">  
  
    <!-- Sets the path to the directory where cache .data files are created.  
  
         If the path is a Java System Property it is replaced by  
         its value in the running VM.  
  
         The following properties are translated:  
         user.home - User`s home directory  
         user.dir - User`s current working directory  
         java.io.tmpdir - Default temp file path -->  
    <!-- 快取寫入檔案目錄 -->  
    <diskStore path="user.home"/>  
  
  
    <!--Default Cache configuration. These will applied to caches programmatically created through  
        the CacheManager.  
  
        The following attributes are required for defaultCache:  
  
        maxInMemory       - Sets the maximum number of objects that will be created in memory  
        eternal           - Sets whether elements are eternal. If eternal,  timeouts are ignored and the element  
                            is never expired.  
        timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only used  
                            if the element is not eternal. Idle time is now - last accessed time  
        timeToLiveSeconds - Sets the time to live for an element before it expires. Is only used  
                            if the element is not eternal. TTL is now - creation time  
        overflowToDisk    - Sets whether elements can overflow to disk when the in-memory cache  
                            has reached the maxInMemory limit.  
  
        -->  
    <!-- 資料過期策略 -->  
    <defaultCache  
            maxElementsInMemory="10000"  
            eternal="false"  
            timeToIdleSeconds="120"  
            timeToLiveSeconds="120"  
            overflowToDisk="true"  
    />  
  
    <!--Predefined caches.  Add your cache configuration settings here.  
        If you do not have a configuration for your cache a WARNING will be issued when the  
        CacheManager starts  
  
        The following attributes are required for defaultCache:  
  
        name              - Sets the name of the cache. This is used to identify the cache. It must be unique.  
        maxInMemory       - Sets the maximum number of objects that will be created in memory  
        eternal           - Sets whether elements are eternal. If eternal,  timeouts are ignored and the element  
                            is never expired.  
        timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only used  
                            if the element is not eternal. Idle time is now - last accessed time  
        timeToLiveSeconds - Sets the time to live for an element before it expires. Is only used  
                            if the element is not eternal. TTL is now - creation time  
        overflowToDisk    - Sets whether elements can overflow to disk when the in-memory cache  
                            has reached the maxInMemory limit.  
  
        -->  
  
    <!-- Sample cache named sampleCache1  
        This cache contains a maximum in memory of 10000 elements, and will expire  
        an element if it is idle for more than 5 minutes and lives for more than  
        10 minutes.  
  
        If there are more than 10000 elements it will overflow to the  
        disk cache, which in this configuration will go to wherever java.io.tmp is  
        defined on your system. On a standard Linux system this will be /tmp"  
        -->  
  
    <!-- Sample cache named sampleCache2  
        This cache contains 1000 elements. Elements will always be held in memory.  
        They are not expired. -->  
    <!-- <cache name="sampleCache2"  
        maxElementsInMemory="1000"  
        eternal="true"  
        timeToIdleSeconds="0"  
        timeToLiveSeconds="0"  
        overflowToDisk="false"  
        />  -->  
  
    <!-- Place configuration for your caches following -->  
  
</ehcache>
複製程式碼

執行測試程式碼會發現兩次執行只有一次sql查詢

查詢快取

前兩種都是對主鍵查詢時候的快取,對於普通的sql查詢我們可以在repository中配置hints

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
複製程式碼
package cn.com.egova.easyshare.server.repository;  
  
import cn.com.egova.easyshare.common.entity.Human;  
import cn.com.egova.easyshare.common.entity.NavMenu;  
import org.springframework.data.jpa.repository.Query;  
import org.springframework.data.jpa.repository.QueryHints;  
import org.springframework.data.repository.PagingAndSortingRepository;  
import org.springframework.data.repository.query.Param;  
  
import javax.persistence.QueryHint;  
import java.util.List;  
  
/** 
 * Created by gongxufan on 2016/3/13.
 */  
public interface SysNavRepository extends PagingAndSortingRepository<NavMenu, Integer> {  
  
    @Query("select n from NavMenu n where exists (select 1 from SysNavRight s " +  
            "where n.menuID=s.sysNavMenuID and s.humanID=:humanId) order by n.displayOrder")  
    @QueryHints({ @QueryHint(name = "org.hibernate.cacheable", value ="true") })  
    List<NavMenu> findMenuList(@Param("humanId") Integer humanId);  
  
    @Query("select m from NavMenu  m  order by m.displayOrder")  
    List<NavMenu> findAllMenuList();  
}
複製程式碼

執行測試程式碼

1
2
3
4
5
複製程式碼
 @Test  
public void queryCacheTest() throws Exception{  
    List<NavMenuDto> navMenuDtos = humanService.getMenuList(37);  
    List<NavMenuDto> navMenuDtos2 = humanService.getMenuList(37);  
}
複製程式碼

控制檯輸出

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
複製程式碼
Hibernate:   
    select  
        navmenu0_.menuID as menuID1_4_,  
        navmenu0_.displayOrder as displayOrder2_4_,  
        navmenu0_.hrefUrl as hrefUrl3_4_,  
        navmenu0_.menuDescr as menuDescr4_4_,  
        navmenu0_.menuIconClass as menuIconClass5_4_,  
        navmenu0_.menuName as menuName6_4_,  
        navmenu0_.seniorMenuID as seniorMenuID7_4_   
    from  
        TCSYSNAVMENU navmenu0_   
    where  
        exists (  
            select  
                1   
            from  
                TCHUMANSYSRIGHTS sysnavrigh1_   
            where  
                navmenu0_.menuID=sysnavrigh1_.sysNavMenuID   
                and sysnavrigh1_.humanID=?  
        )   
    order by  
        navmenu0_.displayOrder  
2016-07-20 14:32:13 INFO main org.hibernate.engine.internal.StatisticalLoggingSessionEventListener - Session Metrics {
    445013 nanoseconds spent acquiring 1 JDBC connections;  
    0 nanoseconds spent releasing 0 JDBC connections;  
    179244800 nanoseconds spent preparing 1 JDBC statements;  
    15228160 nanoseconds spent executing 1 JDBC statements;  
    0 nanoseconds spent executing 0 JDBC batches;  
    20012371 nanoseconds spent performing 39 L2C puts;  
    0 nanoseconds spent performing 0 L2C hits;  
    145920 nanoseconds spent performing 1 L2C misses;  
    0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);  
    0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)  
}  
2016-07-20 14:32:13 INFO main org.hibernate.engine.internal.StatisticalLoggingSessionEventListener - Session Metrics {
    0 nanoseconds spent acquiring 0 JDBC connections;  
    0 nanoseconds spent releasing 0 JDBC connections;  
    0 nanoseconds spent preparing 0 JDBC statements;  
    0 nanoseconds spent executing 0 JDBC statements;  
    0 nanoseconds spent executing 0 JDBC batches;  
    0 nanoseconds spent performing 0 L2C puts;  
    1620055 nanoseconds spent performing 39 L2C hits;  
    36694 nanoseconds spent performing 2 L2C misses;  
    0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);  
    0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)  
}
複製程式碼

這可以看到第二次執行同樣的查詢會有L2C快取命中

總結

如何配置二級快取和注意事項

  1. 首先需要新增pom依賴
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    複製程式碼
    <dependency>  
        <groupId>org.springframework.data</groupId>  
        <artifactId>spring-data-jpa</artifactId>  
        <version>${spring-data-jpa.version}</version>  
    </dependency>  
    <dependency>  
        <groupId>org.hibernate</groupId>  
        <artifactId>hibernate-core</artifactId>  
        <version>${hibernate.version}</version>  
    </dependency>  
    <dependency>  
        <groupId>org.hibernate</groupId>  
        <artifactId>hibernate-ehcache</artifactId>  
        <version>${hibernate.version}</version>  
    </dependency>
    複製程式碼

一定要注意ehcache和hibernate的版本要一致,不然啟動會報錯。

  1. 修改jpa配置
    1
    2
    3
    4
    5
    6
    7
    複製程式碼
    <prop key="hibernate.generate_statistics">true</prop>  
    <!-- 配置二級快取 -->  
    <prop key="hibernate.cache.use_second_level_cache">true</prop>  
    <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>  
    <!-- 開啟查詢快取 -->  
    <prop key="hibernate.cache.use_query_cache">true</prop>  
    <prop key="hibernate.cache.provider_configuration">classpath:ehcache.xml</prop>
    複製程式碼

這裡開啟了二級快取和查詢快取,指定了ehcache快取的配置檔案statistics是個除錯開關,可以查詢快取的命中情況

  1. ehcache.xml配置
    1
    2
    3
    4
    5
    6
    7
    複製程式碼
    <defaultCache  
               maxElementsInMemory="10000"  
               eternal="false"  
               timeToIdleSeconds="120"  
               timeToLiveSeconds="120"  
               overflowToDisk="true"  
       />
    複製程式碼

timeToIdleSeconds
這個是快取的最大空閒時間,也就是多久不訪問自動失效
timeToLiveSeconds
這個是最大存活時間,如果在存活時間內空閒時間達到上限,快取也會自動失效。
4)給entity配置快取策略

1
2
複製程式碼
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)  
@Cacheable(true)
複製程式碼

cacheable主要對集合類的快取提供支援,快取策略這裡適合資料更新不多的情況進行設定。

end

使用二級快取和查詢快取要注意使用場景,如果發現增加快取機制系統反而出現吞吐下降頻發當機。就要考慮是不是該換memcache等專門的快取伺服器了。

相關文章