寶貝徹底搞懂 SpringBoot2.0+Redis+Mybatis實現數據緩存以及緩存註解的使用,

 

1、在pom.xml中引入相關依賴

自己導入web,mybatis,mysql的依賴

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

2、配置redisconfig,在實際項目中可以通過配置KeyGenerator來指定緩存信息的key的生成規則

@Configuration
public class MyRedisConfig {

    @Bean
    public RedisTemplate<Object, Employee> empRedisTemplate(RedisConnectionFactory redisConnectionFactory)throws UnknownHostException {
        RedisTemplate<Object,Employee> template = new RedisTemplate<Object, Employee>();
        template.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer<Employee> ser = new Jackson2JsonRedisSerializer<Employee>(Employee.class);
        template.setDefaultSerializer(ser);
        return template;
    }
    // @Primary將某個緩存管理器作爲默認的
    @Bean
    public RedisCacheManager employeeRedisCacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheConfiguration cacheConfiguration =
                RedisCacheConfiguration.defaultCacheConfig()
                        .entryTtl(Duration.ofDays(1))   // 設置緩存過期時間爲一天
                        .disableCachingNullValues()     // 禁用緩存空值,不緩存null校驗
                        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new
                                GenericJackson2JsonRedisSerializer()));     // 設置CacheManager的值序列化方式爲json序列化,可加入@Class屬性
        return RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(cacheConfiguration).build();     // 設置默認的cache組件
    }


    //可以CacheManagerCuetomizers可以來定製緩存一些規則
    /*@Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        //初始化一個RedisCacheWriter
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
        //設置CacheManager的值序列化方式爲json序列化
        RedisSerializer<Object> jsonSerializer = new GenericJackson2JsonRedisSerializer();
        RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair
                .fromSerializer(jsonSerializer);
        RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig()
                .serializeValuesWith(pair);
        //設置默認超過期時間是30秒
        defaultCacheConfig.entryTtl(Duration.ofSeconds(300));
        //初始化RedisCacheManager
        return new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
    }*/

    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
 
 
        // 使用Jackson2JsonRedisSerialize 替換默認序列化
        @SuppressWarnings("rawtypes")
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
 
 
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
 
 
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
 
 
        // 設置value的序列化規則和 key的序列化規則
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

	
    
    //緩存管理器
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory lettuceConnectionFactory) {
        RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig();
        // 設置緩存管理器管理的緩存的默認過期時間
        defaultCacheConfig = defaultCacheConfig.entryTtl(Duration.ofMinutes(60))
                // 不緩存空值
                .disableCachingNullValues();

        Set<String> cacheNames = new HashSet<>();
        cacheNames.add("my-redis-cache1");

        // 對每個緩存空間應用不同的配置
        Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
        configMap.put("my-redis-cache1", defaultCacheConfig.entryTtl(Duration.ofMinutes(50)));

        RedisCacheManager cacheManager = RedisCacheManager.builder(lettuceConnectionFactory)
                .cacheDefaults(defaultCacheConfig)
                .initialCacheNames(cacheNames)
                .withInitialCacheConfigurations(configMap)
                .build();
        return cacheManager;
    }



}

3、配置文件 application

spring.datasource.url=jdbc:mysql://ip:3306/springboot_cache?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# 開啓駝峯命名匹配
mybatis.configuration.map-underscore-to-camel-case=true

logging.level.com.atguigu.chche.mapper=debug

#debug=true

spring.redis.host=ip

4 配置啓動類-啓動緩存

@SpringBootApplication
@MapperScan("com.atguigu.cache.mapper")
@EnableCaching
public class Boot01CacheApplication {

    public static void main(String[] args) {
        SpringApplication.run(Boot01CacheApplication.class, args);
    }

}

5 緩存註解@Cacheable、@CacheEvict、@CachePut詳解

5.1 註解使用的地方

用在方法上表示:該方法的返回值將被緩存起來。
用在類上表示:表示該類的所有方法都支持該註解。

5.2 key的生成策略


key屬性是用來指定Spring緩存方法的返回結果時對應的key的。該屬性支持SpringEL表達式。當我們沒有指定該屬性時,Spring將使用默認策略生成key。
自定義策略是指我們可以通過Spring的EL表達式來指定我們的key。這裏的EL表達式可以使用方法參數及它們對應的屬性。使用方法參數時我們可以直接使用“#參數名”或者“#p參數index”。下面是幾個使用參數作爲key的示例。

  @Cacheable(value="users", key="#id")

   public User find(Integer id) {

      returnnull;

   }
   @Cacheable(value="users", key="#p0")

   public User find(Integer id) {

      returnnull;

   }
   @Cacheable(value="users", key="#user.id")

   public User find(User user) {

      returnnull;

   }
   @Cacheable(value="users", key="#p0.id")

   public User find(User user) {

      returnnull;

   }

5.3 root對象的使用

除了上述使用方法參數作爲key之外,Spring還爲我們提供了一個root對象可以用來生成key。通過該root對象我們可以獲取到以下信息。

5.4 @CachePut
在支持Spring Cache的環境下,對於使用@Cacheable標註的方法,Spring在每次執行前都會檢查Cache中是否存在相同key的緩存元素,如果存在就不再執行該方法,而是直接從緩存中獲取結果進行返回,否則纔會執行並將返回結果存入指定的緩存中。@CachePut也可以聲明一個方法支持緩存功能。與@Cacheable不同的是使用@CachePut標註的方法在執行前不會去檢查緩存中是否存在之前執行過的結果,而是每次都會執行該方法,並將執行結果以鍵值對的形式存入指定的緩存中。 一般使用在保存,更新方法中。

5.5 @CacheEvict
@CacheEvict是用來標註在需要清除緩存元素的方法或類上的。當標記在一個類上時表示其中所有的方法的執行都會觸發緩存的清除操作。@CacheEvict可以指定的屬性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的語義與@Cacheable對應的屬性類似。即value表示清除操作是發生在哪些Cache上的(對應Cache的名稱);key表示需要清除的是哪個key,如未指定則會使用默認策略生成的key;condition表示清除操作發生的條件。下面我們來介紹一下新出現的兩個屬性allEntries和beforeInvocation。

allEntries屬性
allEntries是boolean類型,表示是否需要清除緩存中的所有元素。默認爲false,表示不需要。當指定了allEntries爲true時,Spring Cache將忽略指定的key。有的時候我們需要Cache一下清除所有的元素,這比一個一個清除元素更有效率。

beforeInvocation屬性
清除操作默認是在對應方法成功執行之後觸發的,即方法如果因爲拋出異常而未能成功返回時也不會觸發清除操作。使用beforeInvocation可以改變觸發清除操作的時間,當我們指定該屬性值爲true時,Spring會在調用該方法之前清除緩存中的指定元素。該屬性表示的是是否在方法執行前就清空,缺省爲 false,如果指定爲 true,則在方法還沒有執行的時候就清空緩存,缺省情況下,如果方法執行拋出異常,則不會清空緩存。


6. 緩存註解的使用

package com.atguigu.cache.service;

import com.atguigu.cache.bean.Employee;
import com.atguigu.cache.mapper.EmployeeMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;

/**
 * Created with IntelliJ IDEA.
 * Author: Coffee君
 * Time: 2020/3/17 11:11
 * Description: No Description
 */
@CacheConfig(cacheNames="emp") // 抽取緩存的公共配置
@Service
public class EmployeeService {

    @Autowired
    EmployeeMapper employeeMapper;

    /*
    * 將方法的運行結果進行緩存;以後再要相同的數據,直接從緩存中獲取,不再調用方法;
    *
    * CacheManager管理多個Cache組件的,對緩存的真正crud操作再Chche組件中,每一個緩存組件中有自己唯一一個名字;
    * 幾個屬性:
    *   cacheNames/value:制定緩存的名字;將方法的返回解惑放在哪個緩存中,是數組的方式,可以指定多個緩存;
    *
    *
    *   key:緩存數據使用的key;可以用它指定。默認是使用方法參數的值 1-方法的返回值
    *           編寫spel; #id;參數id的值 #a0 #p0     key= "#root.methodName+'['+#id+'}'"
    *   keyGenerator: key的生成器;可以自己制定key的生成器的組件id
    *       key/keyGenertor 二選一使用
    *   cacheManger: 制定緩存管理器; 或者指定緩存解析器
    *   condition: 指定符合條件的情況下才緩存
    *       condition = "#a0>1":第一個參數的值》1的時候才緩存
    *
    *   unless:否定緩存;當unless制定的條件爲true,方法的返回值就不會緩存,可以對獲取的結果進行判斷
    *       unless = “result == null”
    *   sysc:是否使用異步模式
    *
    *原理:
    *   1、自動配置類;CacheAuto
    *
    * */
    @Cacheable(/*value =  {"emp"}*/ /*,key= "#root.methodName+'['+#id+'}'" ,condition = "#a0>1",unless = "#a0==2"*/)
    public Employee getEmp(Integer id){
        System.out.println("查詢"+id+"號員工");
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }

    /*
    * @CachePut: 既調用方法,有更新緩存數據;同步更新緩存
    * 修改了數據庫的某個數據,同時更新緩存;
    * 運行時機:
    * 1、先調用目標方法
    * 2、將目標方法的結果緩存起來
    *
    * 測試步驟:
    *   1、查詢1號員工;查到的結果會放在緩存中
    *   2、以後查詢還是之前的結果
    *   3、更新1號員工;
    *   4、查詢1號員工
    *       應該是更新後的員工;
    *           key="employee.id":使用傳入的參數的員工id
    *           key="result.id":使用返回後的id
    *               @Cacheable的key是不能用#result
    *       爲甚麼是沒更新前【1號員工沒有在緩存中更新】
    *
    * */
    @CachePut(/*value = "emp" ,*/ key = "#employee.id")
    public Employee updateEmp(Employee employee){
        System.out.println("updateEmp:"+employee);
        employeeMapper.updateEmp(employee);
        return employee;
    }

    /*
    * @CacheEvict:緩存清除
    *   key:指定要清楚的數據
    *   allEntries = true:指定清除這個緩存中所有數據
    *   beforeInvocation = false: 緩存的清楚是否在方法之前執行
    *       默認代表緩存清除操作是在方法執行之後執行;如果出現異常緩存就不會清楚
    *   beforeInvocation = true:
    *       代表清楚緩存操作是在方法運行之前執行,無論方法是否出現異常,緩存都清除
    *
    * */
    @CacheEvict(/*value = "emp",*/ /*key ="#id",*/beforeInvocation = true)
    public void deleteEmp(Integer id){
        System.out.println("deleteEmp:"+id);
        int i = 10/0;
    }
    //  @Caching 定義複雜的緩存規則
    @Caching(
            cacheable = {
                    @Cacheable(/*value = "emp",*/key = "#lastName")
            },
            put = {
                    @CachePut(/*value = "emp",*/key = "#result.id"),
                    @CachePut(/*value = "emp",*/key = "#result.email")
            }
    )
    public Employee getEmpByLastName(String lastName){
        return employeeMapper.getEmpByLastName(lastName);

    }

}
package com.shenju;

import java.util.ArrayList;
import java.util.List;

import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class CacheServiceImpl {

	/**
	 * 1 這裏面的targetClass=#root.targetClass methodName=#root.methodName
	 * 2 存儲的key爲 user::class com.shenju.CacheServiceImpl:findUserId_wuk
	 * 
	 * @return
	 */
	@Cacheable(value = "user", key = "targetClass + ':' + methodName + '_' + #p0.name")
	public int findUserId(User user) {

		System.out.println("執行find方法.....");
		return 1;
	}

	/**
	 * 1 #p0 表示第一個參數 
	 * 2 存儲的key爲 user::class com.shenju.CacheServiceImpl:listUser_2 
	 * 3 unless 緩存條件:判斷unless,如果返回false,則放入緩存 
	 * 4 #result 表示的是返回結果
	 */
	@Cacheable(value = "user", key = "targetClass + ':' + methodName + '_' + #p0", unless = "#result.size() <= 0")
	public List<User> listUser(int pageNum, int pageSize) {
		
		System.out.println("執行listUser方法。。。。");
		List<User> users = new ArrayList<>();
		users.add(new User("zhengsan", 22));
		users.add(new User("lisi", 20));

		return users;
	}

	/**
	 * 1 存儲的key爲: user::class com.shenju.CacheServiceImpl:findUser_wuk
	 * 2 condition 表示的是緩存的條件,只有當爲true
	 * 
	 */
	@Cacheable(value = "user", key = "targetClass + ':' + methodName + '_' + #p0.name", condition="#user.age==25")
	public User  findUser(User user) {
		
		System.out.println("執行findUser方法。。。。");
		user.setName("wukaikai");
		return user;
	}

	
	
	@CachePut(value = "user", key = "targetClass + ':' + methodName + '_' + #p0.name")
	public User  save(User user) {
		
		System.out.println("執行save方法。。。。");		
		return user;
	}
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章