springboot redis緩存功能的初步瞭解和使用

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

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-redis</artifactId>
			<version>1.4.3.RELEASE</version>
		</dependency>

redis的安裝就不說了,在springboot中導入這兩個包。

在application入口加上這個註解@EnableCaching

@SpringBootApplication
@ServletComponentScan
@RestController
@EnableCaching
public class Application {

配置redis緩存,說實話是在網上找的。

package com.kq.highnet2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;


/**
 * Redis緩存配置類
 * @author szekinwin
 *
 */
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport{

    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.timeout}")
    private int timeout;
    
    //自定義緩存key生成策略
//    @Bean
//    public KeyGenerator keyGenerator() {
//        return new KeyGenerator(){
//            @Override
//            public Object generate(Object target, java.lang.reflect.Method method, Object... params) {
//                StringBuffer sb = new StringBuffer();
//                sb.append(target.getClass().getName());
//                sb.append(method.getName());
//                for(Object obj:params){
//                    sb.append(obj.toString());
//                }
//                return sb.toString();
//            }
//        };
//    }
    //緩存管理器
    @Bean 
    public CacheManager cacheManager(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) {
        RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
        //設置緩存過期時間 
        cacheManager.setDefaultExpiration(10000);
        return cacheManager;
    }
    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory){
        StringRedisTemplate template = new StringRedisTemplate(factory);
        setSerializer(template);//設置序列化工具
        template.afterPropertiesSet();
        return template;
    }
     private void setSerializer(StringRedisTemplate template){
            @SuppressWarnings({ "rawtypes", "unchecked" })
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
            ObjectMapper om = new ObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(om);
            template.setValueSerializer(jackson2JsonRedisSerializer);
     }
}

然後從網上下載redis desktop manager,可以查看redis


這個還是很有必要的,這樣就很好地瞭解緩存的具體位置,順便一提,已經使用了spring-session,作爲分佈式服務器的通用session


下面就是具體的使用了,總共有五個註解@Cacheable、@CacheEvict、@CachePut、@Caching、@CacheConfig

因爲此類內容網上一大堆,我也就不搬運了,就說一下我個人的使用心得和感受吧,這些標籤都有通用值,重要的有value和key

@Cacheable(value="baseUserModel",key="#p0")
BaseUserModel findByUserId(String baseUserId);

結合數據庫管理器就很清楚了


把數據保存到了baseUserModel裏面,並且以方法的參數爲key。就是說緩存是以方法的參數作爲key值而不能以返回值的某一項(比如id)作爲key值,保存緩存和刪除緩存同樣如此。實際上這樣侷限性很大,因爲很多方法並不是用id作爲參數的,而是通過多項條件做爲參數的。

緩存最重要的一點就是你必須可以修改緩存或者刪除它,這種時機的把握很重要,如果只能用方法的參數作爲key鍵,而更新操作一般使用的參數與查詢不同,所以實際上最適用的範圍還是使用id查詢單個對象,我想到的另一種方法就是專門創建刪除緩存的方法,專門傳入查詢的參數,加上@CachePut或@CacheEvict註解。比如:

		@CacheEvict(value="userList",key= "#account.concat(#gender)")
		void deleteCache(String account, String gender);
或者
	@CachePut(value="baseUserModel",key="#baseUser.userId")
	public BaseUserModel updateCache(BaseUserModel baseUser) {
		return baseUser;
	}

要注意的是@CachePut需要返回值,不然就會刪除緩存,雖然也沒什麼關係就是了。

我有個業務需要從好幾個不同的表裏拿出數據拼接起來,所以cache註解需要加載dao層上,然後發現一個問題

@Cacheable(value="baseDepartmentModel",key="#departmentId")
在接口上使用 #參數名 的形式是不行的,它只會拿到null,而是要使用
@Cacheable(value="baseDepartmentModel",key="#p0")
@Cacheable(value="baseDepartmentModel",key="#root.args[0].userId")

通過參數的位置去拿。

又發現一個問題我有一個用戶表BaseUserModel和僱員表BaseEmployeeModel,僱員表直接使用的是用戶表的id,結果當我直接使用

    @Cacheable(value="baseEmployeeModel",key="#p0")
	BaseEmployeeModel findByEmployeeId(String baseUserId);

竟然報轉化錯誤了

java.lang.ClassCastException: com.kq.highnet2.framework.base.baseUser.model.BaseUserModel cannot be cast to com.kq.highnet2.framework.base.baseUser.model.BaseEmployeeModel
	at com.sun.proxy.$Proxy166.findByEmployeeId(Unknown Source)
	at com.kq.highnet2.framework.base.baseUser.service.impl.BaseUserMainServiceImpl.getUserById(BaseUserMainServiceImpl.java:706)
	at com.kq.highnet2.framework.base.baseUser.service.impl.BaseUserMainServiceImpl$$FastClassBySpringCGLIB$$8709dccc.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
	at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)

也就是說redis在查找緩存時,不是先通過value找到對應位置再通過key來獲得對象,而是直接通過key來找到對象


那麼註解的value值實際上並沒有什麼意義,最多起到一個管理key值的作用,那麼如果不同對象擁有相同key值的情況怎麼辦呢?在前面加上專門的前綴就行了,實際上在寫html給id取名時也是如此,加上足夠複雜的前綴或後綴防止重複

    @Cacheable(value="baseEmployeeModel",key="'employee'+#p0")
	BaseEmployeeModel findByEmployeeId(String baseUserId);


之前主管讓我不要使用關聯查詢的語句,現在的用意我也知道了,當使用緩存的時候,直接保存關聯查詢的結果,那麼一旦某個類更新了,則需要刪除所有關聯查詢語句的緩存。

比如,我的計劃單,任務單,都有產品的id,之前使用關聯查詢拿到產品的名稱,一旦產品改變名字了,如果我使用緩存,那麼所有的緩存都要刪除,重新查詢。但是如果我吧每個對象獨立保存,使用的時候根據id查詢出產品,然後賦值給相應的字段,那麼我更新產品時也只要更新產品的緩存就行了。雖然侷限性還是很大的

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