Spring Data Redis 实践以及可视化工具使用

news2024/12/22 18:54:23

目录

一、安装 Redis

Win 环境安装

Linux 环境安装

Redis可视化管理工具

二、Spring Data Redis的使用

Spring Cache简介

Spring Cache常用注解

@EnableCaching

@Cacheable

@CachePut

@CacheEvict

Spring Cache使用步骤

Redis存储JSON格式数据

使用Redis连接池

Jedis vs Lettuce

RedisTemplate 操作 Redis

RedisService

RedisServiceImpl

RedisController


Spring Data Redis 是 Spring 框架提供的用于操作 Redis 的方式 。

 

一、安装 Redis

Win 环境安装

  • 下载完后解压到指定目录;

  • 在当前地址栏输入cmd命令后,使用如下命令可以启动Redis服务;

redis-server.exe redis.windows.conf

  • 如果你想把Redis注册为系统服务来使用的话可以试试下面的命令。

# 安装为服务
redis-server --service-install redis.windows.conf
# 启动服务
redis-server --service-start 
# 停止服务
redis-server --service-stop
# 卸载服务
redis-server --service-uninstall

Linux 环境安装

这里我们使用Docker环境下的安装方式,目前Redis的最新稳定版本为7.x。

  • 下载Redis 7.x的Docker镜像;

docker pull redis:7

  • 使用Docker命令启动Redis容器。

docker run -p 6379:6379 --name redis \
-v /mydata/redis/data:/data \
-d redis:7 redis-server --appendonly yes

Redis可视化管理工具

这里推荐使用Redis官方可视化管理工具 RedisInsight,界面炫酷且功能强大,目前唯一能支持Redis增强功能的工具。

  • 首先下载RedisInsight的安装包,下载地址:RedisInsight - The Best Redis GUI

 

当然除了这个工具外,还推荐其他两个可视化软件,同样很好用 :

 

更快、更好、更稳定的Redis桌面(GUI)管理客户端,兼容Windows、Mac、Linux,性能出众,轻松加载海量键值 地址:Another Redis Desktop Manager | 更快、更好、更稳定的Redis桌面(GUI)管理客户端,兼容Windows、Mac、Linux,性能出众,轻松加载海量键值

Quick Redis :QuickOfficial - QuickRedis (quick123.net)

二、Spring Data Redis的使用

Spring Cache简介

当SpringBoot结合Redis来作为缓存使用时,最简单的方式就是使用Spring Cache了,使用它我们无需知道Spring中对Redis的各种操作,仅仅通过它提供的@Cacheable 、@CachePut 、@CacheEvict 、@EnableCaching等注解就可以实现缓存功能。

Spring Cache常用注解

@EnableCaching

开启缓存功能,一般放在启动类上。

@Cacheable

使用该注解的方法当缓存存在时,会从缓存中获取数据而不执行方法,当缓存不存在时,会执行方法并把返回结果存入缓存中。一般使用在查询方法上,可以设置如下属性:

  • value:缓存名称(必填),指定缓存的命名空间;
  • key:用于设置在命名空间中的缓存key值,可以使用SpEL表达式定义;
  • unless:条件符合则不缓存;
  • condition:条件符合则缓存。

@CachePut

使用该注解的方法每次执行时都会把返回结果存入缓存中。一般使用在新增方法上,可以设置如下属性:

  • value:缓存名称(必填),指定缓存的命名空间;
  • key:用于设置在命名空间中的缓存key值,可以使用SpEL表达式定义;
  • unless:条件符合则不缓存;
  • condition:条件符合则缓存。

@CacheEvict

使用该注解的方法执行时会清空指定的缓存。一般使用在更新或删除方法上,可以设置如下属性:

  • value:缓存名称(必填),指定缓存的命名空间;
  • key:用于设置在命名空间中的缓存key值,可以使用SpEL表达式定义;
  • condition:条件符合则清空缓存。

Spring Cache使用步骤

  • pom.xml中添加项目依赖;
<!--redis依赖配置-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  • 修改配置文件application.yml,添加Redis的连接配置;
spring:
  redis:
    host: localhost # Redis服务器地址
    database: 0 # Redis数据库索引(默认为0)
    port: 6379 # Redis服务器连接端口
    password: # Redis服务器连接密码(默认为空)
    timeout: 1000ms # 连接超时时间

yml 文件总体如下: 

server:
  port: 8080

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/XXXXX?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root
    password: XXXXX
  redis:
    host: localhost # Redis服务器地址
    database: 0 # Redis数据库索引(默认为0)
    port: 6379 # Redis服务器连接端口
    password: # Redis服务器连接密码(默认为空)
    timeout: 3000ms # 连接超时时间
    lettuce:
      pool:
        max-active: 8 # 连接池最大连接数
        max-idle: 8 # 连接池最大空闲连接数
        min-idle: 0 # 连接池最小空闲连接数
        max-wait: -1ms # 连接池最大阻塞等待时间,负值表示没有限制
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher

mybatis:
  mapper-locations:
    - classpath:dao/*.xml
    - classpath*:com/**/mapper/*.xml

logging:
  level:
    root: info
    com.example.generator: debug

springfox:
  documentation:
    # 是否启用Swagger扫描代码生成文档
    enabled: true
    open-api:
      # 是否启用Swagger的open-api
      enabled: false
    swagger-ui:
      # 是否启用Swagger的Web UI
      enabled: true
      # 配置文档基础路径,此时路径为:http://localhost:8080/doc/swagger-ui/index.html
      base-url: /doc

  • 在启动类上添加@EnableCaching注解启动缓存功能;
@EnableCaching
@SpringBootApplication
public class MallTinyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MallTinyApplication.class, args);
    }

}

 或者在 RedisConfig 文件中增加这个注解也可以

  • 接下来在PmsBrandServiceImpl类中使用相关注解来实现缓存功能,可以发现我们获取品牌详情的方法中使用了@Cacheable注解,在修改和删除品牌的方法上使用了@CacheEvict注解;
/**
 * @description PmsBrandService实现类
 */
@Service
public class PmsBrandServiceImpl implements PmsBrandService {
    @Autowired
    private PmsBrandMapper brandMapper;

    @CacheEvict(value = RedisConfig.REDIS_KEY_DATABASE, key = "'pms:brand:'+#id")
    @Override
    public int update(Long id, PmsBrand brand) {
        brand.setId(id);
        return brandMapper.updateByPrimaryKeySelective(brand);
    }

    @CacheEvict(value = RedisConfig.REDIS_KEY_DATABASE, key = "'pms:brand:'+#id")
    @Override
    public int delete(Long id) {
        return brandMapper.deleteByPrimaryKey(id);
    }

    @Cacheable(value = RedisConfig.REDIS_KEY_DATABASE, key = "'pms:brand:'+#id", unless = "#result==null")
    @Override
    public PmsBrand getItem(Long id) {
        return brandMapper.selectByPrimaryKey(id);
    }

}

以及增加对应的 controller 层代码

/**
 * @description 品牌管理Controller
 */
@Controller
@Api(tags = "PmsBrandController")
@Tag(name = "PmsBrandController", description = "商品品牌管理")
@RequestMapping("/brand")
public class PmsBrandController {
    @Autowired
    private PmsBrandService brandService;

    private static final Logger LOGGER = LoggerFactory.getLogger(PmsBrandController.class);

    @ApiOperation("添加品牌")
    @RequestMapping(value = "/create", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult create(@RequestBody PmsBrand pmsBrand) {
        CommonResult commonResult;
        int count = brandService.create(pmsBrand);
        if (count == 1) {
            commonResult = CommonResult.success(pmsBrand);
            LOGGER.debug("create success:{}", pmsBrand);
        } else {
            commonResult = CommonResult.failed("操作失败");
            LOGGER.debug("create failed:{}", pmsBrand);
        }
        return commonResult;
    }

    @ApiOperation("更新指定id品牌信息")
    @RequestMapping(value = "/update/{id}", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult update(@PathVariable("id") Long id, @RequestBody PmsBrand pmsBrandDto, BindingResult result) {
        CommonResult commonResult;
        int count = brandService.update(id, pmsBrandDto);
        if (count == 1) {
            commonResult = CommonResult.success(pmsBrandDto);
            LOGGER.debug("update success:{}", pmsBrandDto);
        } else {
            commonResult = CommonResult.failed("操作失败");
            LOGGER.debug("update failed:{}", pmsBrandDto);
        }
        return commonResult;
    }

    @ApiOperation("删除指定id的品牌")
    @RequestMapping(value = "/delete/{id}", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult delete(@PathVariable("id") Long id) {
        int count = brandService.delete(id);
        if (count == 1) {
            LOGGER.debug("delete success :id={}", id);
            return CommonResult.success(null);
        } else {
            LOGGER.debug("delete failed :id={}", id);
            return CommonResult.failed("操作失败");
        }
    }

    @ApiOperation("获取指定id的品牌详情")
    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<PmsBrand> getItem(@PathVariable("id") Long id) {
        return CommonResult.success(brandService.getItem(id));
    }

    @ApiOperation("分页查询品牌列表")
    @RequestMapping(value = "/list", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<CommonPage<PmsBrand>> list(@RequestParam(value = "pageNum", defaultValue = "1")
                                                        @ApiParam("页码") Integer pageNum,
                                                   @RequestParam(value = "pageSize", defaultValue = "3")
                                                        @ApiParam("每页数量") Integer pageSize) {
        List<PmsBrand> brandList = brandService.list(pageNum, pageSize);
        return CommonResult.success(CommonPage.restPage(brandList));
    }

    @ApiOperation("获取所有品牌列表")
    @RequestMapping(value = "listAll", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<List<PmsBrand>> getAll() {
        return CommonResult.success(brandService.ListAll());
    }
}

程序运行后,先新增一个产品: 

新增产品成功:

然后可以通过生成的ID查找到对应的商品信息:

此时,点击刷新,可以在可视化软件中看到缓存在redis中的数据了

Redis存储JSON格式数据

让Redis存储的标准的JSON格式数据有利于我们接下来的数据处理,由于不设置过期时间容易产生很多不必要的缓存数据,所以我们还需要设置一定的过期时间。

  • 我们可以通过给RedisTemplate设置JSON格式的序列化器,并通过配置RedisCacheConfiguration设置超时时间,此时别忘了去除启动类上的@EnableCaching注解,具体配置类RedisConfig代码如下;
/**
 * @description Redis配置类
 */
@EnableCaching
@Configuration
public class RedisConfig {

    /**
     * redis数据库自定义key
     */
    public  static final String REDIS_KEY_DATABASE="mall";

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisSerializer<Object> serializer = redisSerializer();
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(serializer);
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(serializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    @Bean
    public RedisSerializer<Object> redisSerializer() {
        //创建JSON序列化器
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        //必须设置,否则无法将JSON转化为对象,会转化成Map类型
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(objectMapper);
        return serializer;
    }

    @Bean
    public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
        //设置Redis缓存有效期为1天
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer())).entryTtl(Duration.ofDays(1));
        return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
    }

}
  • 此时我们调用获取商品详情的接口进行测试,会发现Redis中已经缓存了标准的JSON格式数据,并且超时时间被设置为了1天(86400 S)。

使用Redis连接池

SpringBoot 1.5.x版本Redis客户端默认是Jedis实现的,SpringBoot 2.x版本中默认客户端是用Lettuce实现的,我们先来了解下Jedis和Lettuce客户端。

Jedis vs Lettuce

Jedis在实现上是直连Redis服务,多线程环境下非线程安全,除非使用连接池,为每个 RedisConnection 实例增加物理连接。

Lettuce是一种可伸缩,线程安全,完全非阻塞的Redis客户端,多个线程可以共享一个RedisConnection,它利用Netty NIO框架来高效地管理多个连接,从而提供了异步和同步数据访问方式,用于构建非阻塞的反应性应用程序。

  • 修改application.yml添加Lettuce连接池配置,用于配置线程数量和阻塞等待时间。
spring:
  redis:
    lettuce:
      pool:
        max-active: 8 # 连接池最大连接数
        max-idle: 8 # 连接池最大空闲连接数
        min-idle: 0 # 连接池最小空闲连接数
        max-wait: -1ms # 连接池最大阻塞等待时间,负值表示没有限制

RedisTemplate 操作 Redis

        Spring Cache给我们提供了操作Redis缓存的便捷方法,但是也有很多局限性。比如说我们想单独设置一个缓存值的有效期怎么办?我们并不想缓存方法的返回值,我们想缓存方法中产生的中间值怎么办?而且@Cacheable 避免不了脏读?只能通过有效时间来降低脏读的概率。(A读了,B通过@CacheEvict清理,那A的情况类似于脏读)所以,对于使用@Cacheable来获取的数据,在修改或删除这类数据时,可以使用@CacheEvict来进行数据清理,这样就使用@Cacheable的方法就会从数据库里重新查询数据了,但是还是很麻烦。

此时我们就需要用到RedisTemplate这个类了,接下来我们来讲下如何通过RedisTemplate来自由操作Redis中的缓存。

RedisService

定义Redis操作业务类,在Redis中有几种数据结构,比如普通结构(对象),Hash结构、Set结构、List结构,该接口中定义了大多数常用操作方法。

/**
 * @description redis操作Service
 */
public interface RedisService {

    /**
     * 保存属性
     */
    void set(String key, Object value, long time);

    /**
     * 保存属性
     */
    void set(String key, Object value);

    /**
     * 获取属性
     */
    Object get(String key);

    /**
     * 删除属性
     */
    Boolean del(String key);

    /**
     * 批量删除属性
     */
    Long del(List<String> keys);

    /**
     * 设置过期时间
     */
    Boolean expire(String key, long time);

    /**
     * 获取过期时间
     */
    Long getExpire(String key);

    /**
     * 判断是否有该属性
     */
    Boolean hasKey(String key);

    /**
     * 按delta递增
     */
    Long incr(String key, long delta);

    /**
     * 按delta递减
     */
    Long decr(String key, long delta);

    /**
     * 获取Hash结构中的属性
     */
    Object hGet(String key, String hashKey);

    /**
     * 向Hash结构中放入一个属性
     */
    Boolean hSet(String key, String hashKey, Object value, long time);

    /**
     * 向Hash结构中放入一个属性
     */
    void hSet(String key, String hashKey, Object value);

    /**
     * 直接获取整个Hash结构
     */
    Map<Object, Object> hGetAll(String key);

    /**
     * 直接设置整个Hash结构
     */
    Boolean hSetAll(String key, Map<String, Object> map, long time);

    /**
     * 直接设置整个Hash结构
     */
    void hSetAll(String key, Map<String, Object> map);

    /**
     * 删除Hash结构中的属性
     */
    void hDel(String key, Object... hashKey);

    /**
     * 判断Hash结构中是否有该属性
     */
    Boolean hHasKey(String key, String hashKey);

    /**
     * Hash结构中属性递增
     */
    Long hIncr(String key, String hashKey, Long delta);

    /**
     * Hash结构中属性递减
     */
    Long hDecr(String key, String hashKey, Long delta);

    /**
     * 获取Set结构
     */
    Set<Object> sMembers(String key);

    /**
     * 向Set结构中添加属性
     */
    Long sAdd(String key, Object... values);

    /**
     * 向Set结构中添加属性
     */
    Long sAdd(String key, long time, Object... values);

    /**
     * 是否为Set中的属性
     */
    Boolean sIsMember(String key, Object value);

    /**
     * 获取Set结构的长度
     */
    Long sSize(String key);

    /**
     * 删除Set结构中的属性
     */
    Long sRemove(String key, Object... values);

    /**
     * 获取List结构中的属性
     */
    List<Object> lRange(String key, long start, long end);

    /**
     * 获取List结构的长度
     */
    Long lSize(String key);

    /**
     * 根据索引获取List中的属性
     */
    Object lIndex(String key, long index);

    /**
     * 向List结构中添加属性
     */
    Long lPush(String key, Object value);

    /**
     * 向List结构中添加属性
     */
    Long lPush(String key, Object value, long time);

    /**
     * 向List结构中批量添加属性
     */
    Long lPushAll(String key, Object... values);

    /**
     * 向List结构中批量添加属性
     */
    Long lPushAll(String key, Long time, Object... values);

    /**
     * 从List结构中移除属性
     */
    Long lRemove(String key, long count, Object value);
}

RedisServiceImpl

RedisService的实现类,使用RedisTemplate来自由操作Redis中的缓存数据。

/**
 * @description redis操作实现类
 */
@Service
public class RedisServiceImpl implements RedisService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public void set(String key, Object value, long time) {
        redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
    }

    @Override
    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    @Override
    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    @Override
    public Boolean del(String key) {
        return redisTemplate.delete(key);
    }

    @Override
    public Long del(List<String> keys) {
        return redisTemplate.delete(keys);
    }

    @Override
    public Boolean expire(String key, long time) {
        return redisTemplate.expire(key, time, TimeUnit.SECONDS);
    }

    @Override
    public Long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    @Override
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    @Override
    public Long incr(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, delta);
    }

    @Override
    public Long decr(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, -delta);
    }

    @Override
    public Object hGet(String key, String hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }

    @Override
    public Boolean hSet(String key, String hashKey, Object value, long time) {
        redisTemplate.opsForHash().put(key, hashKey, value);
        return expire(key, time);
    }

    @Override
    public void hSet(String key, String hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }

    @Override
    public Map<Object, Object> hGetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    @Override
    public Boolean hSetAll(String key, Map<String, Object> map, long time) {
        redisTemplate.opsForHash().putAll(key, map);
        return expire(key, time);
    }

    @Override
    public void hSetAll(String key, Map<String, Object> map) {
        redisTemplate.opsForHash().putAll(key, map);
    }

    @Override
    public void hDel(String key, Object... hashKey) {
        redisTemplate.opsForHash().delete(key, hashKey);
    }

    @Override
    public Boolean hHasKey(String key, String hashKey) {
        return redisTemplate.opsForHash().hasKey(key, hashKey);
    }

    @Override
    public Long hIncr(String key, String hashKey, Long delta) {
        return redisTemplate.opsForHash().increment(key, hashKey, delta);
    }

    @Override
    public Long hDecr(String key, String hashKey, Long delta) {
        return redisTemplate.opsForHash().increment(key, hashKey, -delta);
    }

    @Override
    public Set<Object> sMembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    @Override
    public Long sAdd(String key, Object... values) {
        return redisTemplate.opsForSet().add(key, values);
    }

    @Override
    public Long sAdd(String key, long time, Object... values) {
        Long count = redisTemplate.opsForSet().add(key, values);
        expire(key, time);
        return count;
    }

    @Override
    public Boolean sIsMember(String key, Object value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }

    @Override
    public Long sSize(String key) {
        return redisTemplate.opsForSet().size(key);
    }

    @Override
    public Long sRemove(String key, Object... values) {
        return redisTemplate.opsForSet().remove(key, values);
    }

    @Override
    public List<Object> lRange(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }

    @Override
    public Long lSize(String key) {
        return redisTemplate.opsForList().size(key);
    }

    @Override
    public Object lIndex(String key, long index) {
        return redisTemplate.opsForList().index(key, index);
    }

    @Override
    public Long lPush(String key, Object value) {
        return redisTemplate.opsForList().rightPush(key, value);
    }

    @Override
    public Long lPush(String key, Object value, long time) {
        Long index = redisTemplate.opsForList().rightPush(key, value);
        expire(key, time);
        return index;
    }

    @Override
    public Long lPushAll(String key, Object... values) {
        return redisTemplate.opsForList().rightPushAll(key, values);
    }

    @Override
    public Long lPushAll(String key, Long time, Object... values) {
        Long count = redisTemplate.opsForList().rightPushAll(key, values);
        expire(key, time);
        return count;
    }

    @Override
    public Long lRemove(String key, long count, Object value) {
        return redisTemplate.opsForList().remove(key, count, value);
    }
}

RedisController

测试RedisService中缓存操作的Controller,大家可以调用测试下。

/**
 * @description Redis测试Controller
 */
@Api(tags = "RedisController", description = "Redis测试")
@Controller
@RequestMapping("/redis")
public class RedisController {
    @Autowired
    private RedisService redisService;
    @Autowired
    private PmsBrandService brandService;

    @ApiOperation("测试简单缓存")
    @RequestMapping(value = "/simpleTest", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<PmsBrand> simpleTest() {
        List<PmsBrand> brandList = brandService.list(1, 5);
        PmsBrand brand = brandList.get(0);
        String key = "redis:simple:" + brand.getId();
        redisService.set(key, brand);
        PmsBrand cacheBrand = (PmsBrand) redisService.get(key);
        return CommonResult.success(cacheBrand);
    }

    @ApiOperation("测试Hash结构的缓存")
    @RequestMapping(value = "/hashTest", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<PmsBrand> hashTest() {
        List<PmsBrand> brandList = brandService.list(1, 5);
        PmsBrand brand = brandList.get(0);
        String key = "redis:hash:" + brand.getId();
        Map<String, Object> value = BeanUtil.beanToMap(brand);
        redisService.hSetAll(key, value);
        Map<Object, Object> cacheValue = redisService.hGetAll(key);
        PmsBrand cacheBrand = BeanUtil.toBean(cacheValue, PmsBrand.class);
        return CommonResult.success(cacheBrand);
    }

    @ApiOperation("测试Set结构的缓存")
    @RequestMapping(value = "/setTest", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<Set<Object>> setTest() {
        List<PmsBrand> brandList = brandService.list(1, 5);
        String key = "redis:set:all";
        redisService.sAdd(key, (Object[]) ArrayUtil.toArray(brandList, PmsBrand.class));
        redisService.sRemove(key, brandList.get(0));
        Set<Object> cachedBrandList = redisService.sMembers(key);
        return CommonResult.success(cachedBrandList);
    }

    @ApiOperation("测试List结构的缓存")
    @RequestMapping(value = "/listTest", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<List<Object>> listTest() {
        List<PmsBrand> brandList = brandService.list(1, 5);
        String key = "redis:list:all";
        redisService.lPushAll(key, (Object[]) ArrayUtil.toArray(brandList, PmsBrand.class));
        redisService.lRemove(key, 1, brandList.get(0));
        List<Object> cachedBrandList = redisService.lRange(key, 0, 3);
        return CommonResult.success(cachedBrandList);
    }
}

 依次对以下示例进行测试:

得到的结果是(举例): 

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

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

相关文章

使用WebSocket协议调用群发方法将消息返回客户端页面

目录 一.C/S架构&#xff1a; 二.Http协议与WebSocket协议的区别&#xff1a; 1.Http协议与WebSocket协议的区别&#xff1a; 2.WebSocket协议的使用场景&#xff1a; 三.项目实际操作&#xff1a; 1.导入依赖&#xff1a; 2.通过WebSocket实现页面与服务端保持长连接&a…

Python包管理工具pip

1、安装pip cmd管理员模式打开控制台 python -m pip install --upgrade pip 2、添加pip环境变量 pip 路径 C:\Users\1\AppData\Local\Programs\Python\Python312\Scripts

若依 Vue3 前端分离 3.8.8 版集成 jsencrypt 实现密码加密传输方式

一、问题展示 在若依的 Vue 前端分离版中登录密码和修改密码时可以发现密码是明文传输的&#xff0c;超管重置用户密码时同样如此&#xff0c;如下图所示&#xff1a; 可以发现密码全部都是明文传输&#xff0c;十分不安全&#xff0c;必须对传输的密码进行加密传输。 二、解决…

前端渲染模式

渲染的概念 在Web开发中&#xff0c;渲染&#xff08;Rendering&#xff09;是一个核心概念&#xff0c;指的是将应用程序的数据&#xff08;data&#xff09;与模板&#xff08;template&#xff09;结合&#xff0c;生成最终的HTML页面&#xff0c;这个页面随后会被浏览器解析…

扰动观测器DOB设计及其MATLAB/Simulink实现

扰动观测器(Disturbance Observer, DOB)是一种在控制系统中用于估计和补偿未知扰动的重要工具,以增强系统的鲁棒性和稳定性。其设计过程涉及系统建模、观测器结构设计以及控制律的调整。 扰动观测器设计原理 系统建模: 首先,需要建立被控对象的数学模型,明确系统的状态变…

深入理解synchronized(简记)

深入理解synchronized 管程synchronized对象的内存布局锁状态记录锁对象状态转换偏向锁轻量级锁锁对象转换总结 管程synchronized Java 参考了 MESA 模型&#xff0c;语言内置的管程&#xff08;synchronized&#xff09;对 MESA 模型进行了精简。 对象的内存布局 对象头 Mar…

DuckDB核心模块揭秘 | 第1期 | 向量化执行引擎之Pipeline

DuckDB核心模块揭秘 | 第1期 | 向量化执行引擎之Pipeline DuckDB是一款非常火的OLAP嵌入式数据库&#xff0c;性能超级棒。它分为多个组件&#xff1a;解析器、逻辑规划器、优化器、物理规划器、执行器以及事务和存储管理层。其中解析器原语PgSQL的解析器&#xff1b;逻辑规划器…

skynet热更新之inject

游戏服务器的热更新是一种常见的需求&#xff0c;skynet可以通过inject的方式&#xff0c;来修改一个服务的消息处理函数&#xff0c;达到热更新的效果。 skynet内置服务debug_console skynet自带了一个调试控制台服务。inject注入代码需要先启动这个服务。 skynet.newservi…

linux自动化构建工具--make/makefile

目录 1.make/makefile介绍 1.1基本认识 1.2依赖关系、依赖方法 1.3具体操作步骤 1.4进一步理解 1.5默认设置 1.6make二次使用的解释 1.7两个文件的时间问题 1.8总是被执行 1.9特殊符号介绍 1.make/makefile介绍 1.1基本认识 make是一个指令&#xff0c;makefile是一…

Dify中语音和文字间转换问题的一种暂时注释方式

本文主要解释了Dify中语音和文字间转换可能会遇到的问题&#xff0c;并给出了一种暂时注释的解决方案。 一.文本转语音可能问题 本地部署文本转语音时&#xff0c;如果遇到如下问题&#xff0c;安装ffmpeg即可。但是如果安装后&#xff0c;重启系统还是遇到这个问题该如何办&…

02 Golang面向对象编程_20240727 课程笔记

视频课程 最近发现越来越多的公司在用Golang了&#xff0c;所以精心整理了一套视频教程给大家&#xff0c;这个是其中的第二部&#xff0c;后续还会有很多。 视频已经录制完成&#xff0c;完整目录截图如下&#xff1a; 课程目录 01 结构体的声明.mp402 使用var根据结构体…

Firefox扩展程序和Java程序通信

实现Firefox扩展程序&#xff0c;和Java RMI Client端进行通信。 在Firefox工具栏注册按钮&#xff0c;点击按钮后弹出Popup.html页面&#xff0c;引用Popup.js脚本&#xff0c;通过脚本向Java RMI client发送消息&#xff0c;Java RMI Client接收消息后转发到Java RMI Server…

Docker————数据卷容器,容器互联,镜像创建

1、Docker的数据管理 管理Docker容器中的数据&#xff0c;主要有两种方式&#xff1a;数据卷&#xff08;Data Volumes&#xff09;和数据卷容器&#xff08;DataVolumes Containers&#xff09;. docker run [-i -t] [--name 容器名] 镜像名&#xff1a;标签 [容器启动命令]…

RK3568 Linux 平台开发系列讲解(内核入门篇):从内核的角度看外设芯片的驱动

在嵌入式 Linux 开发中,外设芯片的驱动是实现操作系统与硬件之间交互的关键环节。对于 RK3568 这样的处理器平台,理解如何从内核的角度构建和管理外设芯片的驱动程序至关重要。 1. 外设驱动的基础概念 外设驱动(Device Driver)是操作系统与硬件设备之间的桥梁。它负责控…

智能浇花机器人·设计说明

智能浇花机器人 目录&#xff1a; 第一章 :项目描述 1 1.1 项目简介 1 1.1.1 服务范围&#xff1a; 1 1.1.2 所处行业&#xff1a; 1 1.2 项目背景 1 1.3 创新点与项目特色 3 第二章 :设计说明书 4 2.1 主要构成&#xff1a; 4 2.1.1 循迹小车 4 2.1.2 机械…

网络编程——wireshark抓包、tcp粘包

目录 一、前言 1.1 什么是粘包 1.2 为什么UDP不会粘包 二、编写程序 文件树 客户端程序 服务器程序 tcp程序 头文件 makefile 三、 实验现象 四、改进实验 五、小作业 一、前言 最近在做网络芯片的驱动&#xff0c;验证功能的时候需要借助wireshark这个工具&…

DataX(二):DataX安装与入门

1. 官方地址 下载地址&#xff1a;http://datax-opensource.oss-cn-hangzhou.aliyuncs.com/datax.tar.gz 源码地址&#xff1a;GitHub - alibaba/DataX: DataX是阿里云DataWorks数据集成的开源版本。 2. 前置要求 Linux JDK(1.8 以上&#xff0c;推荐 1.8) Python(推荐 Pyt…

C语言内存函数精讲

目录 引言 1.内存分配函数malloc 2.内存释放函数free 3.内存拷贝函数memcpy 4.内存移动函数memmove 5.内存设置函数memset 6.内存比较函数memcmp 总结 引言 在C语言编程中&#xff0c;内存管理是核心技能之一。C语言提供了一系列内存操作函数&#xff0c;这些函数在动…

机器学习 第7章-贝叶斯分类器

机器学习 第7章-贝叶斯分类器 7.1 贝叶斯决策论 贝叶斯决策论(Bayesian decision theory)是概率框架下实施决策的基本方法。对分类任务来说&#xff0c;在所有相关概率都已知的理想情形下&#xff0c;贝叶斯决策论考虑如何基于这些概率和误判损失来选择最优的类别标记。下面我…

Linux Vim全能攻略:实战代码,轻松掌握文本编辑神器

1. Vim简介与安装 1.1 Vim的历史与发展 Vim&#xff08;Vi IMproved&#xff09;是一款高度可配置的文本编辑器&#xff0c;它起源于1976年由Bill Joy开发的Vi编辑器。Vi是Unix系统上最古老的文本编辑器之一&#xff0c;因其强大的功能和高效的编辑方式而广受欢迎。随着时间的…