Redis学习------实战篇----2024/02/28

news2025/2/26 11:35:45

1.集群的session共享问题

在这里插入图片描述

2.基于Redis实现共享session登录

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


//4.保存验证码到redis
        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY+phone,code,LOGIN_CODE_TTL, TimeUnit.MINUTES);

RedisTemplate
RedisTemplate使用的是JdkSerializationRedisSerializer存入数据,会将数据先序列化成字节数组,然后在存入Redis数据库。

如果数据是复杂的对象类型,而取出的时候又不想做任何的数据转换,直接从Redis里面取出一个对象,那么使用RedisTemplate是更好的选择

你会看到你的数据不是以可读的形式展现的,而是以字节数组显示

当然从Redis获取数据的时候,也会默认将数据当做字节数组转化,这样就会导致一个问题,当需要获取的数据,不是以字节数组存在redis当中,而是正常的可读的字符串的时候,

RedisTemplate就无法获取导数据,这个时候获取到的值就是NULL。这个时候StringRedisTempate就派上了用场。

StringRedisTemplate使用的是StringRedisSerializer,当你的redis数据库里面本来存的是字符串数据,或者你要存取的数据就是字符串类型数据的时候,那么你就使用StringRedisTemplate即可。

当redis中存入的数据是可读形式而非字节数组时,使用redisTemplate取值的时候会无法获取导出数据,获得的值为null。可以使用 StringRedisTemplate 试试。

文章来源


出现报错
java.lang.ClassCastException: class java.lang.Long cannot be cast to class java.lang.String (java.lang.Long and java.lang.String are in module java.base of loader ‘bootstrap’)
at org.springframework.data.redis.serializer.StringRedisSerializer.serialize(StringRedisSerializer.java:36) ~[spring-data-redis-2.3.9.RELEASE.jar:2.3.9.RELEASE]

在这里插入图片描述
原因是这个类的属性是Long,不是String类型

解决方法
Map<String, Object> userMap = BeanUtil.beanToMap(userDTO,
new HashMap<>(),
CopyOptions.create()
.setIgnoreNullValue(true)
.setFieldValueEditor((fieldName,fieldValue)->fieldValue.toString()));


在这里插入图片描述

这段代码是使用了 BeanUtil 类中的 beanToMap 方法将一个 userDTO 对象转换为一个 Map<String, Object> 类型的对象。在转换过程中,使用了 CopyOptions 类的 create 方法创建了一些配置选项,包括忽略空值和字段值编辑器。

在这里,setIgnoreNullValue(true) 表示忽略空值,即在转换过程中忽略掉值为 null 的属性;setFieldValueEditor((fieldName,fieldValue)->fieldValue.toString()) 则表示对字段值进行编辑,将字段值转换为字符串类型。

通过这段代码,可以将 userDTO 对象的属性值以键值对的形式存储到 userMap 中,且在此过程中满足了忽略空值和字段值编辑的需求。


登录后的数据存储
在这里插入图片描述

    /**
     * 短信验证码登录
     * @param loginForm
     * @param session
     * @return
     */
     @Override
public Result login(LoginFormDTO loginForm, HttpSession session) {
        //1.校验手机号
        String phone = loginForm.getPhone();
        //2.如果不符合,返回错误信息
        if(RegexUtils.isPhoneInvalid(phone)){
            return Result.fail("手机号格式错误");
        }
        //3.校验验证码
        String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
        String code = loginForm.getCode();
        //4.不一致报错
        if(code==null || !cacheCode.toString().equals(code)){
            return Result.fail("验证码错误");
        }
        //5.根据手机号查询用户是否存在
        User user = query().eq("phone", phone).one();
        if (user == null) {
            //6.不存在,创建新用户
            user=createUserWithPhone(phone);
        }
        //7.保存用户到redis中
        //7.1随机生成token,作为登录令牌
        String token = UUID.randomUUID().toString(true);
        //7.2将User对象转换为Hash存储
        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
        Map<String, Object> userMap = BeanUtil.beanToMap(userDTO,
                new HashMap<>(),
                CopyOptions.create()
                        .setIgnoreNullValue(true)
                        .setFieldValueEditor((fieldName,fieldValue)->fieldValue.toString()));
        //7.3存储
        String tokenKey=LOGIN_USER_KEY+token;
        stringRedisTemplate.opsForHash().putAll(tokenKey,userMap);
        //7.4设置token有效期
        stringRedisTemplate.expire(tokenKey,LOGIN_USER_TTL,TimeUnit.MINUTES);
        //8.返回token
        return Result.ok(token);
    }

LoginInterceptor.java的代码

    private StringRedisTemplate stringRedisTemplate;

    public LoginInterceptor(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        //1. 获取请求头中的token
        String token = request.getHeader("authorization");
        if ( StrUtil.isBlank(token)) {
            response.setStatus(401);
            return false;
        }
        String key=RedisConstants.LOGIN_USER_KEY + token;
        //2. 基于token获取redis中的用户
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash()
                .entries(key);

        //3.判断用户是否存在
        if(userMap.isEmpty()){
            //4.用户不存在进行拦截
            response.setStatus(401);
            return false;
        }
        //5.查询到的Hash数据转换为UserDto对象
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);

        // 6.用户存在将用户信息保存到ThreadLocal
        UserHolder.saveUser((UserDTO) userDTO);
        //7. 刷新token的有效期
        stringRedisTemplate.expire(key,RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);
        //8.放行
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }

在这里插入图片描述

3.登录拦截器的优化

在这里插入图片描述
在这里插入图片描述

public class RefreshTokenInterceptor implements HandlerInterceptor {

    private StringRedisTemplate stringRedisTemplate;

    public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        //1. 获取请求头中的token
        String token = request.getHeader("authorization");
        if ( StrUtil.isBlank(token)) {
            return true;
        }
        String key=RedisConstants.LOGIN_USER_KEY + token;
        //2. 基于token获取redis中的用户
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash()
                .entries(key);

        //3.判断用户是否存在
        if(userMap.isEmpty()){
            //4.用户不存在进行拦截
            return true;
        }
        //5.查询到的Hash数据转换为UserDto对象
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);

        // 6.用户存在将用户信息保存到ThreadLocal
        UserHolder.saveUser(userDTO);
        //7. 刷新token的有效期
        stringRedisTemplate.expire(key,RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);
        //8.放行
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }



    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //移除用户
        UserHolder.removeUser();
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

public class LoginInterceptor implements HandlerInterceptor {
    

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

       //1.判断是否需要拦截---ThreadLocal中是否有用户
        if(UserHolder.getUser()==null){
            response.setStatus(401);
            return false;
        }
        return true;
    }



    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //移除用户
        UserHolder.removeUser();
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

4.商户查询缓存

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.添加Redis缓存

在这里插入图片描述

@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Result queryById(Long id) {
        //1.从redis中查询缓存
        String key = CACHE_SHOP_KEY + id;
        String shopJson = stringRedisTemplate.opsForValue().get(key); 
        //2.判断是否存在
        //3.存在则直接返回
        
        if (StrUtil.isNotBlank(shopJson)){
            Shop shop = JSONUtil.toBean(shopJson, Shop.class);
            return Result.ok(shop);
        }
        //4.不存在,根据ID查询数据库
        Shop shop = getById(id);
        //5.不存在,返回错误
        if (shop== null){
            return Result.fail("店铺不存在");
        }
        //6.存在,写入redis
        stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop));
        //7.返回
        return Result.ok(shop);
    }
}

缓存商品列表

@Service
public class ShopTypeServiceImpl extends ServiceImpl<ShopTypeMapper, ShopType> implements IShopTypeService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Override
    public Result queryList() {
        String key = CACHE_TYPE_LIST;
        String shopJson = stringRedisTemplate.opsForValue().get(key);
        //查询缓存中是否有数据
        if(StrUtil.isNotBlank(shopJson)){
            List<ShopType> shopTypeList = JSONUtil.toList(shopJson, ShopType.class);
            return Result.ok(shopTypeList);
        }
        List<ShopType> shopTypeList = query().orderByAsc("sort").list();
        //将数据库中的数据保存到缓存中
        stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shopTypeList));
        return Result.ok(shopTypeList);
    }
}

5.缓存更新策略

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.数据库和缓存的双写一致的实现

 @Override
    @Transactional
    public Result update(Shop shop) {
        Long shopId = shop.getId();
        if (shopId==null){
            return Result.fail("查询的店铺不存在");
        }

        //1.更新数据库
        updateById(shop);
        //2.删除缓存
        stringRedisTemplate.delete(CACHE_SHOP_KEY + shopId);
        return null;
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1477300.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

不看后悔的2024年腾讯云服务器购买优惠活动汇总

腾讯云服务器多少钱一年&#xff1f;62元一年起&#xff0c;2核2G3M配置&#xff0c;腾讯云2核4G5M轻量应用服务器218元一年、756元3年&#xff0c;4核16G12M服务器32元1个月、312元一年&#xff0c;8核32G22M服务器115元1个月、345元3个月&#xff0c;腾讯云服务器网txyfwq.co…

二叉树(C/C++)

本篇将较为详细的介绍二叉树的相关知识&#xff0c;以及二叉树的实现。对于二叉树的相关知识&#xff0c;本篇介绍了其概念、特殊的二叉树、性质还有存储结构。 接着对于实现二叉树的每个函数都有其思路讲解&#xff0c;主要的函数分为&#xff1a;遍历&#xff1a;前中后序遍历…

redis-Redis主从,哨兵和集群模式

一&#xff0c;Redis的主从复制 ​ 主机数据更新后根据配置和策略&#xff0c; 自动同步到备机的master/slaver机制&#xff0c;Master以写为主&#xff0c;Slave以读为主。这样做的好处是读写分离&#xff0c;性能扩展&#xff0c;容灾快速恢复。 1.1 环境搭建 如果你的redi…

【HDFS】Decommision(退役) EC数据节点剩最后几个块卡住的问题

一、背景 近期操作退役EC集群的节点。在退役的过程中,遇到了一些问题。特此总结一下。 本文描述的问题现象是: 每一批次退役10个节点,完全退役成功后开始操作下一批。 但是,中间有一批次有2台节点的Under Replicated Blocks一直是1,不往下降。 处于Decommissioning状态卡…

iOS App冷启动优化:Before Main阶段

iOS应用冷启动时&#xff0c;在 UIApplicationMain(argc, argv, nil, appDelegateClassName)方法执行前&#xff0c;主要经历以下阶段&#xff1a; 1. 执行exec&#xff08;&#xff09;启动应用程序进程 2. 加载可执行文件&#xff0c;即将应用程序的Mach-O文件加载到内存…

跟着野火学FreeRTOS:第二段(堆存储管理)

F r e e R T O S FreeRTOS FreeRTOS从版本 V 9.0.0 V9.0.0 V9.0.0开始&#xff0c;内核对象所用的存储空间可以在编译时静态分配或在运行时动态分配&#xff0c;早期的版本不同时支持静态分配和动态分配&#xff0c;这里讲到的堆存储管理是和动态分配相关的。从版本 V 9.0.0 V9…

跨区域复制建筑UI输入框脚本迷你世界

--复制区域文件 --设置坐标起点&#xff0c;终点 --创建区域 --获取坐标id,data --星空露珠工作室制作 local pos1{x-16,y7,z28} local pos2{x28,y44,z-9} local block{num0} local str{} local str0{} local num0 local count0 local ui6 --几个输入框 local romath.random(…

【LeetCode:108. 将有序数组转换为二叉搜索树 + 二叉树+递归】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

Vue3+springboot实现简单登录demo

Vue3从0搭建脚手架步骤【默认已安装node.js】 前置条件&#xff1a;默认已安装node.js、yarn 第一步&#xff1a;创建项目 选择任意一个空白文件夹如下&#xff1a; cmd进入该文件夹下的命令窗口模式&#xff0c;然后输入指令创建vue项目&#xff1a;vue create my-project …

Maya笔记 大纲视图、父子级、打组

文章目录 大纲视图打开大纲视图双层大纲视图 父子级快捷键大纲视图 组菜单操作快捷键操作 大纲视图 打开大纲视图 双层大纲视图 鼠标放到大纲视图最下边的…上&#xff0c;鼠标变成拖动&#xff0c;向上拖&#xff0c;可以拖出两个大纲视图&#xff0c;方便我们同时操作两个物…

[JavaWeb玩耍日记]HTML与CSS快速使用

目录 一.标签 二.指定css 三.css选择器 四.超链接 五.视频与排版 六.布局测试 七.布局居中 八.表格 九.表单 十.表单项 实验项目目录&#xff1a; 必要文件new.css: h1 {color:black; } span{color: #968D92;; } a {color: #000;text-decoration: none; } p {text-i…

水合三氯化铱(三氯化铱水合物)行业技术壁垒较高 应用范围有望扩展

水合三氯化铱&#xff08;三氯化铱水合物&#xff09;行业技术壁垒较高 应用范围有望扩展 水合三氯化铱又称三氯化铱水合物&#xff0c;分子式为IrCl33H2O&#xff0c;是一种无机化合物。水合三氯化铱外观呈绿色结晶或褐色粉末&#xff0c;可溶于水、丙酮和盐酸&#xff0c;难溶…

学习阶段单片机买esp32还是stm32?

学习阶段单片机买esp32还是stm32? 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「stm32的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xf…

Altair® HyperWorks® 2023 卓越的开放式仿真和设计平台

Altair HyperWorks 2023 卓越的开放式仿真和设计平台 全新的 Altair HyperWorks 2023 软件版本现已发布&#xff01;Altair HyperWorks 2023 是一个开放式仿真和设计平台&#xff0c;功能更强大、用途更广泛&#xff0c;将为工程师提供适用于各行业的全面 CAE 软件套件。通过运…

【appium】Hybrid应用自动化|微信小程序自动化

目录 一、Hybrid&#xff08;nativewebview&#xff09;应用自动化 1、webview 2、Hybrid应用自动化实现 2.1准备工作 Step1&#xff1a;准备android 4.4版本以上的手机/模拟器 Step2&#xff1a;在app源码中将webview调试模式打开 Step3&#xff1a;安装UC开发者工具 U…

Sui主网升级至V1.19.1版本

其他升级要点如下所示&#xff1a; #16190, #16193 现在CLI正确处理并修复了交易没有输入或命令时的输出表格。例如&#xff0c;调用 client call — package 0x2 — module kiosk — function default 现在具有正确格式的输出。 #15928 Move编译器的一系列变更 添加了宏函…

Redis持久化的两种方式RDB和AOF详解

小伙伴们好&#xff0c;欢迎关注&#xff0c;一起学习&#xff0c;无限进步 以下内容为学习 Redis 过程中的笔记 文章目录 Redis持久化RDB&#xff08; Redis DataBase &#xff09;触发机制&#xff1a;如何恢复rbd文件&#xff1a;优点&#xff1a;缺点&#xff1a; AOF &…

搬家了,发现虚拟机链接不上,查找原因,解决了

是网络配置的问题&#xff0c;因为ip地址变动&#xff0c;所以配置文件要进行改动 1.通过cmd查看本地主机ip地址 2.在虚拟网络编辑器中选在vmnet8&#xff0c;用管理员权限修改ip&#xff0c;网关&#xff0c;子网掩码&#xff0c;和物理主机对应 3.在/etc/sysconfig/network…

同局域网共享虚拟机(VMware)

一、前言 首先我们先来了解下 VMware 的三种网络模式桥接模式、NAT模式、仅主机模式&#xff0c;网络类型介绍详情可以参考下我之前的文档 Linux系统虚拟机安装&#xff08;上&#xff09;第三章 - 第9步指定网络类型。了解三种网络模式的原理之后&#xff0c;再来剖析下需求&…

【力扣hot100】刷题笔记Day14

前言 又是新的一周&#xff0c;快乐的周一&#xff0c;快乐地刷题&#xff0c;今天把链表搞完再干活&#xff01; 114. 二叉树展开为链表 - 力扣&#xff08;LeetCode&#xff09; 前序遍历 class Solution:def flatten(self, root: Optional[TreeNode]) -> None:if not r…