Примечания по использованию кеша с spring-data-jpa

задняя часть Spring Hibernate SQL
Примечания по использованию кеша с spring-data-jpa

предисловие

Использование реализации JPA в hibernate очень удобно для простых запросов. Для сложных запросов мы также можем написать SQL для выполнения сложных запросов на объединение нескольких таблиц. Многим людям не нравится спящий режим, потому что они не имеют глубокого понимания его механизма.Если вы внимательно изучите исходный код его реализации, это на самом деле очень приятный процесс обучения. Использование различных дизайнерских парадигм также прекрасно.

Здесь мы в основном говорим о настройке кеша. Поскольку он является спящим, его механизм кэширования неотделим от этих трех типов: кэш уровня сеанса, кэш второго уровня уровня sessionFactory, кэш запросов и кэш с коллекциями. Для использования jpa я лично рекомендую не использовать реляционное отображение. Это приводит к различным запросам на ассоциации, включающим такие механизмы, как ленивая загрузка, которая потребляет много дополнительной работы прокси-сервера cglib. В частности, многие люди также используют org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter для реализации отложенной загрузки страниц коллекции. Это катастрофа для систем с высокими требованиями к ответу http.

Использование jpa, чтобы отказаться от части реляционного отображения в спящем режиме, чтобы сохранить простоту и гибкость разработки.

Сначала протестируйте код

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);  
    }  
  
}

Кэш L1

То есть самостоятельно созданный кеш уровня сеанса.В транзакции диалога сеанса запрос, основанный на первичном ключе, будет запрашивать базу данных только один раз.

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 будет выполнен повторно.

Кэш L2

Кэш второго уровня по умолчанию отключен, и необходимо настроить политику кэширования.

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>

Затем настройте аннотацию кеша в объекте, который должен использовать кеш.

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-запросов мы можем настроить подсказки в репозитории

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.

Суммировать

Как настроить кэш L2 и рекомендации

  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 указывается в качестве переключателя отладки, который может запрашивать состояние попадания кеша.

  1. конфигурация ehcache.xml
    1
    2
    3
    4
    5
    6
    7
    
    <defaultCache  
               maxElementsInMemory="10000"  
               eternal="false"  
               timeToIdleSeconds="120"  
               timeToLiveSeconds="120"  
               overflowToDisk="true"  
       />
    

timeToIdleSeconds
Это максимальное время простоя кеша, то есть сколько времени требуется для автоматической инвалидации
timeToLiveSeconds
Это максимальное время выживания.Если время простоя достигает верхнего предела в пределах времени выживания, кеш также будет автоматически аннулирован.
4) Настройте стратегию кэширования для объекта

1
2
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)  
@Cacheable(true)

Cacheable в основном обеспечивает поддержку кэширования классов коллекций, и здесь задается стратегия кэширования для ситуаций, когда обновлений данных немного.

end

При использовании кеша второго уровня и кеша запросов следует обратить внимание на сценарии использования, если вы обнаружите, что система увеличивает механизм кеша, пропускная способность упадет, и система будет часто падать. Необходимо подумать, не пора ли перейти на специальный кеш-сервер, такой как memcache.