玩转Spring Cache --- @Cacheable使用在MyBatis的Mapper接口上(解决Null key returned for cache operation)【享学Spring】(下)

简介: 玩转Spring Cache --- @Cacheable使用在MyBatis的Mapper接口上(解决Null key returned for cache operation)【享学Spring】(下)

怎么破?


问题已经定位了,我从来不缺解决方案。下面我给小伙伴们介绍三种,任君选择

方案一:使用a0/p0的方式去对方法入参进行引用

说了很多次了,key中使用SpEL表达式,即可用字段名,也可以用a0/p0这种按照顺序的方式去获取,形如这样:


@Cacheable(cacheNames = "demoCache", key = "#a0")


运行一把试试,终于一切正常,并且缓存也生效了:


----------验证缓存是否生效----------
org.springframework.cache.concurrent.ConcurrentMapCache@709ed6f3
User(id=1, name=fsx, age=21)


这种方案使用起来相对非常简单(把控好参数顺序),并且得到了源生支持无需额外开发,所以推荐使用~


方案二:自定义注解 + KeyGenerator


从之前的源码分析知道,如果自己不指定key这个属性,会交给KeyGenerator去自动生成,此方案就是以这个原理为理论基础实现的。


但是,难道需要给每个方法都定一个个性化的KeyGenerator来解决???

当然这样也是一种解决方案,但是也太麻烦了,现在此方案不可能落地的。所以本文需要结合一个自定义注解,绕开key这个属性然后加上一个**通用的KeyGenerator**来解决问题,下面我直接给出示例代码:


1、自定义一个自己的注解,绕过key,提供一个新属性mykey


@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Cacheable
public @interface MyCacheable {
    @AliasFor("cacheNames")
    String[] value() default {};
    @AliasFor("value")
    String[] cacheNames() default {};
    String key() default "";
    String keyGenerator() default "";
    String cacheManager() default "";
    String cacheResolver() default "";
    String condition() default "";
    String unless() default "";
    boolean sync() default false;
  // 自定义的属性,代替key属性(请不要使用key属性了)
    String myKey() default "";
}


2、准备一个通用的KeyGenerator来可以处理自定义注解的myKey属性:


@EnableCaching // 使用了CacheManager,别忘了开启它  否则无效
@Configuration
public class CacheConfig extends CachingConfigurerSupport {
    @Bean(name = "myMethodParamKeyGenerator")
    public KeyGenerator myMethodParamKeyGenerator() {
        return (target, method, params) -> {
            //获得注解
            MyCacheable myCacheable = AnnotationUtils.findAnnotation(method, MyCacheable.class);
            if (myCacheable != null) {
                String myKey = myCacheable.myKey();
                if (myKey != null && StringUtils.hasText(myKey)) {
                    //获取方法的参数集合
                    Parameter[] parameters = method.getParameters();
                    StandardEvaluationContext context = new StandardEvaluationContext();
                    //遍历参数,以参数名和参数对应的值为组合,放入StandardEvaluationContext中
                    // 注意:若没有java8的编译参数-parameters,参数名都回事arg0,arg1...  若有参数就是具体的参数名了
                    for (int i = 0; i < parameters.length; i++) {
                        context.setVariable(parameters[i].getName(), params[i]);
                    }
                    ExpressionParser parser = new SpelExpressionParser();
                    //根据newKey来解析获得对应值
                    Expression expression = parser.parseExpression(myKey);
                    return expression.getValue(context, String.class);
                }
            }
            return params[0].toString();
        };
    }
    ...
}


3、把新注解使用在我们的Mapper接口上:

// @Repository //备注:这个注解是没有必要的    因为已经被@MapperScan扫进去了
public interface CacheDemoMapper {
    @Select("select * from user where id = #{id}")
    @MyCacheable(cacheNames = "demoCache", /*key = "#a0"*/ keyGenerator = "myMethodParamKeyGenerator", myKey = "#arg0")
    User getUserById(Integer id);
}


运行如上测试用例:缓存生效,正常work


此方案我个人也是比较推荐的,不仅仅能解决问题,而且还能达到炫技的效果。(不要说炫技无用,炫技从另外一方面能反映出你的实力,领导才能看中你嘛~~~当然,在生产环境下不要过度炫技)


方案三:开启Java8的-parameters 编译参数


方案二有个弊端,就是只能使用arg0、arg1这种方式引用到入参的值,使用起来不是特别的方便。原因是Java编译器在编译的时候就已经把Method的形参变量名抹去了。若想保留这个值,Java8提供了-parameters编译参数来实现:


此处处理编译以Idea为例,若是用maven编译的话,方式雷同。

步骤:setting-Java Compiler(如下图):


image.png


加入此参数后,编译后再运行。这时,你的myKey就只能这么写了:myKey = "#id"(#变量名x形式) 一切正常~


此种方其实我是并不推荐的,因为它还得强依赖于编译参数,有这种强依赖还是不太好


总结


虽然说程序员最重要的技能是ctrl c加ctrl v。拿来主义固然是好,但是我建议还是得活出差异化,否则怎么脱颖而出呢?因此我是支持狂造你的代码,只有你的花招多了,你才是那个特别的你。


熟悉我写博文的小伙伴应该知道,我很少介绍一种技术的基本使用,而是注重乱造代码,因为我还是比较注重分享稍微高质量一些的知识,也希望前行的路上有你的支持和鼓励

相关文章
|
8天前
|
SQL Java 数据库连接
SpringBoot整合Mybatis
SpringBoot整合Mybatis
34 2
|
6天前
|
算法 Java 数据库连接
Spring+MySQL+数据结构+集合,Alibaba珍藏版mybatis手写文档
Spring+MySQL+数据结构+集合,Alibaba珍藏版mybatis手写文档
|
8天前
|
Java 数据库连接 Spring
Spring 整合mybatis
Spring 整合mybatis
22 2
|
8天前
|
JSON Java 数据格式
nbcio-boot升级springboot、mybatis-plus和JSQLParser后的LocalDateTime日期json问题
nbcio-boot升级springboot、mybatis-plus和JSQLParser后的LocalDateTime日期json问题
12 0
|
8天前
|
SQL Java 数据库连接
15:MyBatis对象关系与映射结构-Java Spring
15:MyBatis对象关系与映射结构-Java Spring
31 4
|
8天前
键值的 key 和 value 允许为null吗
键值的 key 和 value 允许为null吗
|
8天前
|
XML Java 数据库连接
Spring Boot与MyBatis:整合与实战
【4月更文挑战第29天】在现代的Java Web应用开发中,持久化层框架扮演了至关重要的角色。MyBatis作为一款优秀的持久化框架,被广泛应用于Java开发中。Spring Boot提供了简化开发流程的功能,而与MyBatis的整合也变得更加便捷。
29 0
|
8天前
|
SQL Java 数据库连接
挺详细的spring+springmvc+mybatis配置整合|含源代码
挺详细的spring+springmvc+mybatis配置整合|含源代码
100 1
|
8天前
|
druid Java 数据库连接
Spring Boot3整合MyBatis Plus
Spring Boot3整合MyBatis Plus
53 1
|
8天前
|
Java 数据库连接 数据库
spring+mybatis_编写一个简单的增删改查接口
spring+mybatis_编写一个简单的增删改查接口
18 2