玩转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。拿来主义固然是好,但是我建议还是得活出差异化,否则怎么脱颖而出呢?因此我是支持狂造你的代码,只有你的花招多了,你才是那个特别的你。


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

相关文章
|
XML Java 数据库连接
微服务——SpringBoot使用归纳——Spring Boot集成MyBatis——基于 xml 的整合
本教程介绍了基于XML的MyBatis整合方式。首先在`application.yml`中配置XML路径,如`classpath:mapper/*.xml`,然后创建`UserMapper.xml`文件定义SQL映射,包括`resultMap`和查询语句。通过设置`namespace`关联Mapper接口,实现如`getUserByName`的方法。Controller层调用Service完成测试,访问`/getUserByName/{name}`即可返回用户信息。为简化Mapper扫描,推荐在Spring Boot启动类用`@MapperScan`注解指定包路径避免逐个添加`@Mapper`
822 0
|
10月前
|
Java 数据库连接 数据库
Spring boot 使用mybatis generator 自动生成代码插件
本文介绍了在Spring Boot项目中使用MyBatis Generator插件自动生成代码的详细步骤。首先创建一个新的Spring Boot项目,接着引入MyBatis Generator插件并配置`pom.xml`文件。然后删除默认的`application.properties`文件,创建`application.yml`进行相关配置,如设置Mapper路径和实体类包名。重点在于配置`generatorConfig.xml`文件,包括数据库驱动、连接信息、生成模型、映射文件及DAO的包名和位置。最后通过IDE配置运行插件生成代码,并在主类添加`@MapperScan`注解完成整合
1549 1
Spring boot 使用mybatis generator 自动生成代码插件
|
10月前
|
Java 数据库连接 API
Java 对象模型现代化实践 基于 Spring Boot 与 MyBatis Plus 的实现方案深度解析
本文介绍了基于Spring Boot与MyBatis-Plus的Java对象模型现代化实践方案。采用Spring Boot 3.1.2作为基础框架,结合MyBatis-Plus 3.5.3.1进行数据访问层实现,使用Lombok简化PO对象,MapStruct处理对象转换。文章详细讲解了数据库设计、PO对象实现、DAO层构建、业务逻辑封装以及DTO/VO转换等核心环节,提供了一个完整的现代化Java对象模型实现案例。通过分层设计和对象转换,实现了业务逻辑与数据访问的解耦,提高了代码的可维护性和扩展性。
372 1
|
9月前
|
SQL Java 数据库连接
Spring、SpringMVC 与 MyBatis 核心知识点解析
我梳理的这些内容,涵盖了 Spring、SpringMVC 和 MyBatis 的核心知识点。 在 Spring 中,我了解到 IOC 是控制反转,把对象控制权交容器;DI 是依赖注入,有三种实现方式。Bean 有五种作用域,单例 bean 的线程安全问题及自动装配方式也清晰了。事务基于数据库和 AOP,有失效场景和七种传播行为。AOP 是面向切面编程,动态代理有 JDK 和 CGLIB 两种。 SpringMVC 的 11 步执行流程我烂熟于心,还有那些常用注解的用法。 MyBatis 里,#{} 和 ${} 的区别很关键,获取主键、处理字段与属性名不匹配的方法也掌握了。多表查询、动态
269 0
|
9月前
|
存储 缓存 NoSQL
Spring Cache缓存框架
Spring Cache是Spring体系下的标准化缓存框架,支持多种缓存(如Redis、EhCache、Caffeine),可独立或组合使用。其优势包括平滑迁移、注解与编程两种使用方式,以及高度解耦和灵活管理。通过动态代理实现缓存操作,适用于不同业务场景。
683 0
|
10月前
|
SQL Java 数据库
解决Java Spring Boot应用中MyBatis-Plus查询问题的策略。
保持技能更新是侦探的重要素质。定期回顾最佳实践和新技术。比如,定期查看MyBatis-Plus的更新和社区的最佳做法,这样才能不断提升查询效率和性能。
546 1
|
XML Java 数据库连接
微服务——SpringBoot使用归纳——Spring Boot集成MyBatis——基于注解的整合
本文介绍了Spring Boot集成MyBatis的两种方式:基于XML和注解的形式。重点讲解了注解方式,包括@Select、@Insert、@Update、@Delete等常用注解的使用方法,以及多参数时@Param注解的应用。同时,针对字段映射不一致的问题,提供了@Results和@ResultMap的解决方案。文章还提到实际项目中常结合XML与注解的优点,灵活使用两者以提高开发效率,并附带课程源码供下载学习。
967 0
|
缓存 NoSQL Java
Spring Cache 缓存原理与 Redis 实践
Spring Cache 缓存原理与 Redis 实践
712 0
|
缓存 NoSQL Java
spring cache整合redis实现springboot项目中的缓存功能
spring cache整合redis实现springboot项目中的缓存功能
874 1
|
缓存 NoSQL Java
Spring Cache 整合 Redis 做缓存使用~ 快速上手~
Spring Cache 整合 Redis 做缓存使用~ 快速上手~
619 1