Spring-Cache整合Redis

配置类

/**
 * @description: Spring的缓存配置!!!
 */
@Slf4j
@Configuration
@EnableCaching
public class MyCacheConfig extends CachingConfigurerSupport {
	/** 人员每日打卡的缓存名称 */
    public static final String CACHE_NAME_PERSON_DAILY_GROUP= "PERSON_DAILY_GROUP";

    @Resource
    private RedisConnectionFactory redisConnectionFactory;

    private CacheManager cacheManager;

    @Override
    @Bean
    public CacheManager cacheManager() {
        final RedisCacheManager redisCacheManager = new RedisCacheManager(
                // 创建无锁的RedisCacheWriter对象
                RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory),
                // 默认的缓存配置,未配置的 cacheName 会使用这个
                this.createCacheConfiguration(30 * 60),
                // 针对特定cacheName的缓存配置
                this.initialCacheConfigurations()
        );
        this.cacheManager = redisCacheManager;
        return redisCacheManager;
    }

    /**
     * 创建缓存配置对象
     * @param seconds 缓存的ttl,单位为秒。0表示永久缓存
     * @return  缓存配置对象
     */
    private RedisCacheConfiguration createCacheConfiguration(Integer seconds) {
        // 使用Jackson2JsonRedisSerialize 替换默认序列化(JdkSerializationRedisSerializer)
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        // 下面2行解决jackson2无法反序列化LocalDateTime的问题
        om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        om.registerModule(new JavaTimeModule());
        jackson2JsonRedisSerializer.setObjectMapper(om);

        // 缓存key前缀生成规则
        CacheKeyPrefix cacheKeyPrefix = (String cacheName) -> cacheName + SysConstants.COLON;

        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .entryTtl(Duration.ofSeconds(seconds))
                .computePrefixWith(cacheKeyPrefix);

        return redisCacheConfiguration;
    }

    private Map<String, RedisCacheConfiguration> initialCacheConfigurations() {
        Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
        // key:cacheName,value:缓存配置
        redisCacheConfigurationMap.put(CACHE_NAME_PERSON_DAILY_GROUP, this.createCacheConfiguration(24*60*60));
        return redisCacheConfigurationMap;
    }

    @Override
    @Bean
    public KeyGenerator keyGenerator() {
        return (o, method, objects) -> {
            StringBuilder sb = new StringBuilder();
            sb.append(o.getClass().getName()).append(".");
            sb.append(method.getName()).append(".");
            for (Object obj : objects) {
                sb.append(obj.toString());
            }
            return sb.toString();
        };
    }

    @Override
    @Bean
    public CacheResolver cacheResolver() {
        return new SimpleCacheResolver(this.cacheManager);
    }

    @Override
    @Bean
    public CacheErrorHandler errorHandler() {
        // 用于捕获从Cache中进行CRUD时的异常的回调处理器。
        return new SimpleCacheErrorHandler();
    }
}

若要确保该配置类一定能被扫描到,可以不使用@Configuration方式,而是在/resources/META-INF/spring.factories文件中配置org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.xx.common.yy.config.MyCacheConfig

使用

/**
 * 人员每日打卡 服务实现类
 * @author panxili
 * @since 2022-08-11
 */
@Slf4j
@Service
@CacheConfig(cacheNames = CACHE_NAME_PERSON_DAILY_GROUP)
public class PersonDailyCheckService {

	
	@Cacheable(key = "T(String).join('-'', #queryForm.day, #queryForm.personId, #queryForm.shopOrgId, #queryForm.positionId)"
            , condition = "#queryForm != null && #queryForm.day != null && T(cn.hutool.core.text.CharSequenceUtil).isAllNotEmpty(#queryForm.personId, #queryForm.shopOrgId, #queryForm.positionId)")
	public List<PersonDailyCheckModel> queryList(PersonDailyCheckQueryForm queryForm) {
        log.info("com.xx.service.attendance.service.PersonDailyCheckService.queryList() run...");
        return personDailyCheckMapper.queryList(queryForm);
    }

从数据库中查到数据后,序列化到缓存时报错:

org.springframework.expression.spel.SpelEvaluationException: EL1029E: A problem occurred when trying to execute method 'join' on object of type 'java.lang.Class': 'Problem invoking method: public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.CharSequence[])'
    at org.springframework.expression.spel.ast.MethodReference.getValueInternal(MethodReference.java:145)
    at org.springframework.expression.spel.ast.MethodReference.access$000(MethodReference.java:54)
    at org.springframework.expression.spel.ast.MethodReference$MethodValueRef.getValue(MethodReference.java:390)
    at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:90)
    at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:109)
    at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:265)
    at org.springframework.cache.interceptor.CacheOperationExpressionEvaluator.key(CacheOperationExpressionEvaluator.java:104)
    at org.springframework.cache.interceptor.CacheAspectSupport$CacheOperationContext.generateKey(CacheAspectSupport.java:778)
    at org.springframework.cache.interceptor.CacheAspectSupport.generateKey(CacheAspectSupport.java:575)
    ......
Caused by: org.springframework.expression.AccessException: Problem invoking method: public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.CharSequence[])
    at org.springframework.expression.spel.support.ReflectiveMethodExecutor.execute(ReflectiveMethodExecutor.java:134)
    at org.springframework.expression.spel.ast.MethodReference.getValueInternal(MethodReference.java:138)
    ... 98 common frames omitted
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1001E: Type conversion problem, cannot convert from java.time.LocalDate to java.lang.CharSequence
    at org.springframework.expression.spel.support.StandardTypeConverter.convertValue(StandardTypeConverter.java:75)
    at org.springframework.expression.spel.support.ReflectionHelper.convertArguments(ReflectionHelper.java:307)
    at org.springframework.expression.spel.support.ReflectiveMethodExecutor.execute(ReflectiveMethodExecutor.java:123)
    ... 99 common frames omitted
Caused by: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.time.LocalDate] to type [java.lang.CharSequence]
    at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:321)
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:194)
    at org.springframework.expression.spel.support.StandardTypeConverter.convertValue(StandardTypeConverter.java:70)
    ... 101 common frames omitted

#queryForm.day改为#queryForm.day.toString()

@Cacheable(key = "T(String).join('-'', #queryForm.day.toString(), #queryForm.personId, #queryForm.shopOrgId, #queryForm.positionId)"
            , condition = "#queryForm != null && #queryForm.day != null && T(cn.hutool.core.text.CharSequenceUtil).isAllNotEmpty(#queryForm.personId, #queryForm.shopOrgId, #queryForm.positionId)")

从缓存中查到数据后,反序列化时报错:

org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Unrecognized field "pmOffCheckStatusTimely" (class com.xx.service.attendance.model.PersonDailyCheckModel), not marked as ignorable (28 known properties: "pmOffCheckStatus", "shopOrgId", "amOnCheckTime", "storeLongitude", "shiftStr", "personId", "areaOrgId", "pmOffDutyTime", "needCheckInfoTimelyList", "pmOnCheckTime", "id", "day", "pmOnDutyTime", "companyOrgId", "pmOffCheckTime", "positionId", "createTime", "bigRegionOrgId", "storeLatitude", "amOnCheckStatus", "week", "totalCheckStatus", "checkRecordList", "salesDepartmentId", "groupId", "pmOnCheckStatus", "amOnDutyTime", "updateTime"])
 at [Source: (byte[])"["java.util.ArrayList",[["com.xx.service.attendance.model.PersonDailyCheckModel",{"id":61,"day":"2022-08-26","week":"FRIDAY","personId":"3fda4ad023e74177afb506c621bf9443","companyOrgId":"003539","salesDepartmentId":"003543","bigRegionOrgId":"003546","areaOrgId":"003547","shopOrgId":"003763","storeLongitude":"121.5517810","storeLatitude":"24.9951282","positionId":"0003","groupId":2,"amOnDutyTime":"09:30:00","amOnCheckStatus":"LATE","amOnCheckTime":"2022-08-26T10:08:12","pmOnDutyTime":"14:00:00"[truncated 1118 bytes]; line: 1, column: 822] (through reference chain: java.util.ArrayList[0]->com.xx.service.attendance.model.PersonDailyCheckModel["pmOffCheckStatusTimely"]); nested exception is com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "pmOffCheckStatusTimely" (class com.xx.service.attendance.model.PersonDailyCheckModel), not marked as ignorable (28 known properties: "pmOffCheckStatus", "shopOrgId", "amOnCheckTime", "storeLongitude", "shiftStr", "personId", "areaOrgId", "pmOffDutyTime", "needCheckInfoTimelyList", "pmOnCheckTime", "id", "day", "pmOnDutyTime", "companyOrgId", "pmOffCheckTime", "positionId", "createTime", "bigRegionOrgId", "storeLatitude", "amOnCheckStatus", "week", "totalCheckStatus", "checkRecordList", "salesDepartmentId", "groupId", "pmOnCheckStatus", "amOnDutyTime", "updateTime"])
 at [Source: (byte[])"["java.util.ArrayList",[["com.xx.service.attendance.model.PersonDailyCheckModel",{"id":61,"day":"2022-08-26","week":"FRIDAY","personId":"3fda4ad023e74177afb506c621bf9443","companyOrgId":"003539","salesDepartmentId":"003543","bigRegionOrgId":"003546","areaOrgId":"003547","shopOrgId":"003763","storeLongitude":"121.5517810","storeLatitude":"24.9951282","positionId":"0003","groupId":2,"amOnDutyTime":"09:30:00","amOnCheckStatus":"LATE","amOnCheckTime":"2022-08-26T10:08:12","pmOnDutyTime":"14:00:00"[truncated 1118 bytes]; line: 1, column: 822] (through reference chain: java.util.ArrayList[0]->com.xx.service.attendance.model.PersonDailyCheckModel["pmOffCheckStatusTimely"])
	at org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer.deserialize(Jackson2JsonRedisSerializer.java:75)
	at org.springframework.data.redis.serializer.DefaultRedisElementReader.read(DefaultRedisElementReader.java:48)
	at org.springframework.data.redis.serializer.RedisSerializationContext$SerializationPair.read(RedisSerializationContext.java:226)
	at org.springframework.data.redis.cache.RedisCache.deserializeCacheValue(RedisCache.java:254)
	at org.springframework.data.redis.cache.RedisCache.lookup(RedisCache.java:88)
	at org.springframework.cache.support.AbstractValueAdaptingCache.get(AbstractValueAdaptingCache.java:58)
	at org.springframework.cache.interceptor.AbstractCacheInvoker.doGet(AbstractCacheInvoker.java:73)
	......
Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "pmOffCheckStatusTimely" (class com.xx.service.attendance.model.PersonDailyCheckModel), not marked as ignorable (28 known properties: "pmOffCheckStatus", "shopOrgId", "amOnCheckTime", "storeLongitude", "shiftStr", "personId", "areaOrgId", "pmOffDutyTime", "needCheckInfoTimelyList", "pmOnCheckTime", "id", "day", "pmOnDutyTime", "companyOrgId", "pmOffCheckTime", "positionId", "createTime", "bigRegionOrgId", "storeLatitude", "amOnCheckStatus", "week", "totalCheckStatus", "checkRecordList", "salesDepartmentId", "groupId", "pmOnCheckStatus", "amOnDutyTime", "updateTime"])
 at [Source: (byte[])"["java.util.ArrayList",[["com.xx.service.attendance.model.PersonDailyCheckModel",{"id":61,"day":"2022-08-26","week":"FRIDAY","personId":"3fda4ad023e74177afb506c621bf9443","companyOrgId":"003539","salesDepartmentId":"003543","bigRegionOrgId":"003546","areaOrgId":"003547","shopOrgId":"003763","storeLongitude":"121.5517810","storeLatitude":"24.9951282","positionId":"0003","groupId":2,"amOnDutyTime":"09:30:00","amOnCheckStatus":"LATE","amOnCheckTime":"2022-08-26T10:08:12","pmOnDutyTime":"14:00:00"[truncated 1118 bytes]; line: 1, column: 822] (through reference chain: java.util.ArrayList[0]->com.xx.service.attendance.model.PersonDailyCheckModel["pmOffCheckStatusTimely"])
	at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:61)
	at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownProperty(DeserializationContext.java:823)
	at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:1153)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1589)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1567)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:294)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
	at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._deserialize(AsArrayTypeDeserializer.java:116)
	at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer.deserializeTypedFromAny(AsArrayTypeDeserializer.java:71)
	at com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer$Vanilla.deserializeWithType(UntypedObjectDeserializer.java:712)
	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:288)
	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:245)
	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:27)
	at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._deserialize(AsArrayTypeDeserializer.java:116)
	at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer.deserializeTypedFromAny(AsArrayTypeDeserializer.java:71)
	at com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer$Vanilla.deserializeWithType(UntypedObjectDeserializer.java:712)
	at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:68)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3129)
	at org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer.deserialize(Jackson2JsonRedisSerializer.java:73)
	... 97 common frames omitted

原来是因为实体类中有getPmOffCheckStatusTimely()方法,在序列化时会调用该方法,然后缓存的json中会有pmOffCheckStatusTimely字段。后来反序列化时,在实体类中找不到pmOffCheckStatusTimely属性,所以就报错了!

实体类

@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(description = "人员每日打卡")
public class PersonDailyCheckModel implements Serializable {

	@ApiModelProperty(value = "下午下班应该打卡时间;若无需打卡,则为null")
    private LocalTime pmOffDutyTime;
	
	/**
     * 获取下午下班打卡状态(实时)
     * @return
     */
    @Nullable
    public CheckStatusEnum getPmOffCheckStatusTimely() {
        if (getPmOffDutyTime() == null) {
            return null;
        }

        final CheckStatusEnum pmOffCheckStatus = this.getPmOffCheckStatus();
        if (pmOffCheckStatus != null) {
            return pmOffCheckStatus;
        }

        final LocalDate day = getDay();
        if (LocalDateTime.now().compareTo(day.plusDays(ONE).atTime(AM_ON_CHECK_START)) >= ZERO) {
            return CheckStatusEnum.FORGOT;
        }

        return null;
    }
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值