• SpringBoot +Mybatis + Redis实现缓存(案例解析)



    大家好,我是卷心菜。本篇主要讲解用Redis实现缓存的案例解析,如果您看完文章有所收获,可以三连支持博主哦~,嘻嘻。


    一、前言

    🎁作者简介:在校大学生一枚,Java领域新星创作者,Java、Python正在学习中,期待和大家一起学习一起进步~
    💗个人主页:我是一棵卷心菜的个人主页
    🔶本文专栏:Redis理论和实战
    📕自我提醒:多学多练多思考,编程能力才能节节高!

    • 各位小伙伴们,博主写的Redis专栏有一段时间了,前面讲解了Redis的例如五种常用数据类型、redis实现持久化、主从复制、哨兵模式等等理论知识。接下来,就要开始着重讲解用redis实现各种业务的代码分析和模拟,一起学习吧!

    二、数据库表

    CREATE TABLE `users` (
      `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
      `username` varchar(50) NOT NULL DEFAULT '' COMMENT '用户名',
      `password` varchar(50) NOT NULL DEFAULT '' COMMENT '密码',
      `sex` tinyint(4) NOT NULL DEFAULT '0' COMMENT '性别 0=女 1=男 ',
      `deleted` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT '删除标志,默认0不删除,1删除',
      `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
      `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=1001 DEFAULT CHARSET=utf8 COMMENT='用户表';
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    类型介绍:

    • unsigned:默认的 int 类型,取值范围是 -2147483648~2147483647 之间,而 unsigned 的取值范围是 0~4294967295 之间。默认的 int 类型,允许插入负数,unsigned 设置后,无法插入负数。
    • tinyint:独立使用时,使用范围是0-127。tinyint unsigned的使用范围是0~255的整型数据,存储大小为1字节。如果数字较小比如用0和1表示性别或者表示年龄时,可以用tinyint。
    • timestamp:DEFAULT CURRENT_TIMESTAMP,如插入记录时未指定具体时间数据则将该时间戳字段值设置为当前时间;更新记录时,时间戳字段包含ON UPDATE CURRENT_TIMESTAMP,如更新记录时未指定具体时间数据则将该时间戳字段值设置为当前时间

    三、配置文件

    spring.application.name=spring-boot-mybatis-redis
    server.port=8080
    # mybatis配置
    mybatis.mapper-locations=classpath*:com/cabbage/redis/mapper/xml/*.xml
    # 数据库配置
    spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
    spring.datasource.url=jdbc:mysql://localhost:3306/springboot_redis?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    spring.datasource.username=root
    spring.datasource.password=0000
    # 日志配置
    logging.level.com.cabbage=debug
    # swagger2配置
    spring.swagger2.enabled=true
    # redis配置
    spring.redis.database=0
    spring.redis.host=192.18.5.131
    spring.redis.port=6379
    spring.redis.password=123
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    四、配置类

    swagger2配置类:

    @Configuration
    @EnableSwagger2
    public class SwaggerConfig {
        @Value(value = "${spring.swagger2.enabled}")
        private Boolean swaggerEnabled;
        @Bean
        public Docket createRestApi() {
            return new Docket(DocumentationType.SWAGGER_2)
                    .apiInfo(apiInfo())
                    .enable(swaggerEnabled)
                    .select()
                    .apis(RequestHandlerSelectors.basePackage("com.cabbage.redis"))
                    .paths(PathSelectors.any())
                    .build();
        }
        private ApiInfo apiInfo() {
            return new ApiInfoBuilder()
                    .title("接口文档")
                    .description("Spring Boot - redis")
                    .termsOfServiceUrl("https://cabbage.blog.csdn.net/")
                    .version("1.0")
                    .build();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    redis序列化配置类:

    @Configuration
    public class RedisConfiguration {
        @Bean
        public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
            RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
            redisTemplate.setConnectionFactory(redisConnectionFactory);
            //创建一个json的序列化对象
            GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
            //设置value的序列化方式json
            redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
            //设置key序列化方式string
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            //设置hash key序列化方式string
            redisTemplate.setHashKeySerializer(new StringRedisSerializer());
            //设置hash value的序列化方式json
            redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
            redisTemplate.afterPropertiesSet();
            return redisTemplate;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    注意:

    /**
     * 重写Redis序列化方式,使用Json方式:
     * 当我们的数据存储到Redis的时候,我们的键(key)和值(value)都是通过Spring提供的Serializer序列化到Redis的。
     * RedisTemplate默认使用的是JdkSerializationRedisSerializer,
     * StringRedisTemplate默认使用的是StringRedisSerializer
     * Spring Data JPA为我们提供了下面的Serializer:
     * GenericToStringSerializer、Jackson2JsonRedisSerializer、
     * JacksonJsonRedisSerializer、JdkSerializationRedisSerializer、
     * OxmSerializer、StringRedisSerializer。
     * 在此我们将自己配置RedisTemplate并定义Serializer。
     */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    五、实体类设计

    @Table(name = "users")
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User {
        @Id
        @GeneratedValue(generator = "JDBC")
        private Integer id;
        private String username;
        private String password;
        //性别 0=女 1=男
        private Byte sex;
        //删除标志,默认0不删除,1删除
        private Byte deleted;
        @Column(name = "update_time")
        private Date updateTime;
        @Column(name = "create_time")
        private Date createTime;
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", username='" + username + '\'' +
                    ", password='" + password + '\'' +
                    ", sex=" + sex +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • @Id:建议有一个@Id注解作为主键的字段,可以有多个@Id注解的字段作为联合主键;默认情况下,实体类中如果不存在包含@Id注解的字段,所有的字段都会作为主键字段进行使用(这种效率极低)
    • @GeneratedValue(generator = "JDBC"):这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键

    六、核心代码

    @Service
    public class UserService {
        private static final Logger LOGGER = LoggerFactory.getLogger(UserService.class);
        @Autowired
        private UserMapper userMapper;
        @Autowired
        private RedisTemplate redisTemplate;
        # redis数据库key的前缀
        public static final String CACHE_KEY_USER = "user:";
        
        # 把数据先放入数据库,然后再放入Redis数据中
        public void createUser(User obj) {
            this.userMapper.insertSelective(obj);
            //缓存key
            String key = CACHE_KEY_USER + obj.getId();
            //到数据库里面,重新捞出新数据出来,做缓存
            obj = this.userMapper.selectByPrimaryKey(obj.getId());
            //opsForValue代表了Redis的String数据结构
            //set代表了redis的SET命令
            redisTemplate.opsForValue().set(key, obj);
        }
        
        # 查找数据,实现缓存
        public User findUserById(Integer userid) {
            ValueOperations<String, User> operations = redisTemplate.opsForValue();
            //缓存key
            String key = CACHE_KEY_USER + userid;
            //1.先去redis查 ,如果查到直接返回,没有的话直接去数据库捞
            //Redis 用了GET命令
            User user = operations.get(key);
            //2.redis没有的话,直接去数据库捞
            if (user == null) {
                user = this.userMapper.selectByPrimaryKey(userid);
                //由于redis没有才到数据库捞,所以必须把捞到的数据写入redis,方便下次查询能redis命中。
                operations.set(key, user);
            }
            return user;
        }
        
        public void updateUser(User obj) {
            //1.先直接修改数据库
            this.userMapper.updateByPrimaryKeySelective(obj);
            //2.再修改缓存
            //缓存key
            String key = CACHE_KEY_USER + obj.getId();
            obj = this.userMapper.selectByPrimaryKey(obj.getId());
            //修改也是用SET命令,重新设置,Redis 没有update操作,都是重新设置新值
            redisTemplate.opsForValue().set(key, obj);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    注意:

    • createUser方法中语句:obj = this.userMapper.selectByPrimaryKey(obj.getId());一定要有!是因为参数obj是一个对象,没有具体的时间戳;把obj存入mysql后就有了时间戳,然后才能从数据库中拿出数据,变成JSON格式后存入redis。

    • selectByPrimaryKey():通过主键id查找对象

    • insertSelective():只给有值的字段赋值(会对传进来的值做非空判断)

    • insert():所有的字段都会添加一遍即使没有值

    • updateByPrimaryKeySelective():不会把null值插入数据库,避免覆盖之前有值的。

    • updateByPrimaryKey():会根据传入的对象,全部取值插入数据库,会存在覆盖数据的问题。具体使用哪个函数看场景。


    感谢阅读,一起进步,嘻嘻~

  • 相关阅读:
    mysql转sqlite3实战+部署sqlite3应用
    删除最近7天没有访问的文件
    二叉树的基本概念与操作
    Python学习笔记:Jupyter Notebook快速入门案例:学习时间与成绩的关系
    【iOS开发】(四)react Native第三方组件五个20240419-20
    云服务器哪家便宜?教你怎么在AWS免费领一年云服务器(领取篇)
    【kubernetes】k8s各组件运行流程以及高可用
    Mybatis-plus中更新date类型数据遇到的坑
    测试代码1
    京东商城Java岗4面面经分享,(3轮技术+HR面已拿offer)
  • 原文地址:https://blog.csdn.net/weixin_59654772/article/details/125792416