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


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

相关文章
|
4天前
|
SQL Java 数据库连接
对Spring、SpringMVC、MyBatis框架的介绍与解释
Spring 框架提供了全面的基础设施支持,Spring MVC 专注于 Web 层的开发,而 MyBatis 则是一个高效的持久层框架。这三个框架结合使用,可以显著提升 Java 企业级应用的开发效率和质量。通过理解它们的核心特性和使用方法,开发者可以更好地构建和维护复杂的应用程序。
66 29
|
9天前
|
Shell
Image provider: AssetImage(bundle: null, name: “assets/images/hot.png”) Image key: AssetBundleImageKey(bundle: PlatformAssetBundle#9d9f7(), name: “assets/images/hot.png”, scale: 1) 图像无法加载,并且其他图标图像也出错的解决方案-优雅草卓伊凡
Image provider: AssetImage(bundle: null, name: “assets/images/hot.png”) Image key: AssetBundleImageKey(bundle: PlatformAssetBundle#9d9f7(), name: “assets/images/hot.png”, scale: 1) 图像无法加载,并且其他图标图像也出错的解决方案-优雅草卓伊凡
29 12
|
15天前
|
前端开发 Java 数据库连接
Java后端开发-使用springboot进行Mybatis连接数据库步骤
本文介绍了使用Java和IDEA进行数据库操作的详细步骤,涵盖从数据库准备到测试类编写及运行的全过程。主要内容包括: 1. **数据库准备**:创建数据库和表。 2. **查询数据库**:验证数据库是否可用。 3. **IDEA代码配置**:构建实体类并配置数据库连接。 4. **测试类编写**:编写并运行测试类以确保一切正常。
29 2
|
15天前
|
SQL Java 数据库连接
【潜意识Java】深入理解MyBatis的Mapper层,以及让数据访问更高效的详细分析
深入理解MyBatis的Mapper层,以及让数据访问更高效的详细分析
31 1
|
27天前
|
SQL JavaScript Java
Spring Boot 3 整合 Mybatis-Plus 实现数据权限控制
本文介绍了如何在Spring Boot 3中整合MyBatis-Plus实现数据权限控制,通过使用MyBatis-Plus提供的`DataPermissionInterceptor`插件,在不破坏原有代码结构的基础上实现了细粒度的数据访问控制。文中详细描述了自定义注解`DataScope`的使用方法、`DataPermissionHandler`的具体实现逻辑,以及根据用户的不同角色和部门动态添加SQL片段来限制查询结果。此外,还展示了基于Spring Boot 3和Vue 3构建的前后端分离快速开发框架的实际应用案例,包括项目的核心功能模块如用户管理、角色管理等,并提供Gitee上的开源仓库
150 11
|
2月前
|
缓存 Java 数据库连接
深入探讨:Spring与MyBatis中的连接池与缓存机制
Spring 与 MyBatis 提供了强大的连接池和缓存机制,通过合理配置和使用这些机制,可以显著提升应用的性能和可扩展性。连接池通过复用数据库连接减少了连接创建和销毁的开销,而 MyBatis 的一级缓存和二级缓存则通过缓存查询结果减少了数据库访问次数。在实际应用中,结合具体的业务需求和系统架构,优化连接池和缓存的配置,是提升系统性能的重要手段。
114 4
|
2月前
|
SQL Java 数据库连接
spring和Mybatis的各种查询
Spring 和 MyBatis 的结合使得数据访问层的开发变得更加简洁和高效。通过以上各种查询操作的详细讲解,我们可以看到 MyBatis 在处理简单查询、条件查询、分页查询、联合查询和动态 SQL 查询方面的强大功能。熟练掌握这些操作,可以极大提升开发效率和代码质量。
132 3
|
3月前
|
Java 数据库连接 数据库
spring和Mybatis的逆向工程
通过本文的介绍,我们了解了如何使用Spring和MyBatis进行逆向工程,包括环境配置、MyBatis Generator配置、Spring和MyBatis整合以及业务逻辑的编写。逆向工程极大地提高了开发效率,减少了重复劳动,保证了代码的一致性和可维护性。希望这篇文章能帮助你在项目中高效地使用Spring和MyBatis。
70 1
|
4月前
|
Java 数据库连接 Maven
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和MyBatis Generator,使用逆向工程来自动生成Java代码,包括实体类、Mapper文件和Example文件,以提高开发效率。
192 2
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
|
4月前
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
125 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块