本文介绍: 主要介绍如何在 Spring Boot使用 RedisTemplate 对 Redis 的 JSON 类型进行单个查询模糊搜索设置操作作者通过实例代码和详细解释,让读者深入了解了 Redis基本操作使用技巧,并提供了一些解决 Redis 遇到常见问题方法建议

1. 准备工作

  1. 环境说明

  2. 背景说明

  3. 实现思路采用 RedisTemplate, 执行 lua 脚本

2. 实现

2.1 配置

引入依赖

        <dependency&gt;
            <groupId&gt;org.springframework.boot</groupId&gt;
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

配置 redisTermplate, 配置 lua 脚本,便于 redisTemplate 执行[^2]

@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport
{
    @Bean
    @SuppressWarnings(value = { "unchecked", "rawtypes" })
    public RedisTemplate<String, Object> redisTemplate1(RedisConnectionFactory connectionFactory)
    {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        RedisSearchSerializer serializer = new RedisSearchSerializer(Object.class);

        // 使用StringRedisSerializer来序列化和反序列化rediskey
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);

        // Hashkey采用StringRedisSerializer的序列化方式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);

        template.afterPropertiesSet();
        return template;
    }
    
    // lua 脚本配置
    @Bean
    public DefaultRedisScript<String> jsonSetScript()
    {
        DefaultRedisScript<String> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText("return redis.call('JSON.SET', KEYS[1], '$', ARGV[1]);");
        redisScript.setResultType(String.class);
        return redisScript;
    }

    @Bean
    public DefaultRedisScript<Object> jsonGetScript()
    {
        DefaultRedisScript<Object> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText("return redis.call('JSON.GET', KEYS[1]);");
        redisScript.setResultType(Object.class);
        return redisScript;
    }

    @Bean
    public DefaultRedisScript<List> jsonSearchScript()
    {
        DefaultRedisScript<List> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(
                "local offset = tonumber(ARGV[2])n" +
                        "local count = tonumber(ARGV[3])n" +
                "return redis.call('FT.SEARCH', KEYS[1], ARGV[1], 'return', 0, 'limit', offset, count);");
        redisScript.setResultType(List.class);
        return redisScript;
    }
}

RedisSearchSerializer 序列化配置


import com.alibaba.fastjson2.JSON;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;

import java.nio.charset.Charset;

/**
 * Redis使用FastJson序列化
 *
 * @author ruoyi
 */
public class RedisSearchSerializer<T> implements RedisSerializer<T> {
    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");

    private Class<T> clazz;

    public RedisSearchSerializer(Class<T> clazz) {
        super();
        this.clazz = clazz;
    }

    @Override
    public byte[] serialize(T t) throws SerializationException {
        if (t == null) {
            return new byte[0];
        }
        if (t instanceof String) {
            return ((String)t).getBytes(DEFAULT_CHARSET);
        }
        return JSON.toJSONString(t).getBytes(DEFAULT_CHARSET);
    }

    @Override
    public T deserialize(byte[] bytes) throws SerializationException {
        if (bytes == null || bytes.length <= 0) {
            return null;
        }
        String str = new String(bytes, DEFAULT_CHARSET);
        // 不是 json 也不是 序列化字符串,那就只能是数字,如果不是数字直接返回
        if (!str.startsWith("{") &amp;&amp; !str.startsWith("[") &amp;&amp; !str.startsWith(""") &amp;&amp; !str.matches("^\d*$")) {
            return clazz.cast(str);
        }
        return JSON.parseObject(str, clazz);
    }
}

数据实体类

@Data
public class LoginUser {
    private String ipaddr;
    private String username;

    public LoginUser(String ipaddr, String username) {
        this.ipaddr = ipaddr;
        this.username = username;
    }
}
 

2.2 封装json的 操作

redisService


@Service
public class RedisService {

    @Autowired
    private RedisScript<String> jsonSetScript;
    @Autowired
    private RedisScript<Object> jsonGetScript;
    @Autowired
    private RedisScript<List> jsonSearchScript;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate1;

    public LoginUser getLoginUser(String uuid) {
        String key = RedisKeys.LOGIN_TOKEN_KEY + uuid;
        JSONObject obj = (JSONObject) redisTemplate1.execute(this.jsonGetScript, Collections.singletonList(key));
        if (obj == null) {
            return null;
        }
        return obj.toJavaObject(LoginUser.class);
    }

    public void setLoginUser(String uuid, LoginUser loginUser, int expireTime, TimeUnit unit) {
        String key = RedisKeys.LOGIN_TOKEN_KEY + uuid;
        redisTemplate1.execute(this.jsonSetScript, Collections.singletonList(key), loginUser);
        redisCache.expire(key, expireTime, unit);

    }

    public Page<String> searchLoginUser(String query, Pageable page) {
        List list = redisTemplate1.execute(
                this.jsonSearchScript,
                Collections.singletonList("login_tokens_idx"),
                query, page.getOffset(), page.getPageSize());
        Long total = (Long) list.get(0);
        List<String> ids = new ArrayList<>();
        for (int i = 1; i < list.size(); i++) {
			ids.add(((String) list.get(i)).replaceAll(RedisKeys.LOGIN_TOKEN_KEY, ""));
        }
        return new PageImpl<>(ids, page, total);
    }
    public interface RedisKeys {
	    String LOGIN_TOKEN_KEY = "login_tokens1:";
	}
}

2.3 在 redis 中创建索引

redis 创建索引[^1], 其中 ipaddr 是 IP 字段包含 “.” 等特殊字符,所以需要将 索引中的 ipaddr 设置成 tag 类型,才能搜索到[^3]

# 连接redis, 如果使用 redisinsight 则不需要这步
redis-cli -a "password"
# 创建索引
FT.CREATE login_tokens_idx 
  on JSON prefix 1 "login_tokens1:"
  SCHEMA $.ipaddr tag $.username text

3. 测试


import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.concurrent.TimeUnit;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = YourApplication.class)
@ActiveProfiles("prod-local")
public class RedisServiceTest {
    @Autowired
    private RedisService redisService;

    @Test
    public void testSetAndGetLoginUser() {
        LoginUser user = new LoginUser("192.168.1.1", "testUser");
        redisService.setLoginUser("123456", user, 60, TimeUnit.SECONDS);

        LoginUser getUser = redisService.getLoginUser("123456");
        Assert.assertEquals(user.getIpaddr(), getUser.getIpaddr());
        Assert.assertEquals(user.getUsername(), getUser.getUsername());
    }

    @Test
    public void testDeleteLoginUser() {
        LoginUser user = new LoginUser("192.168.1.1", "testUser");
        redisService.setLoginUser("123456", user, 60, TimeUnit.SECONDS);

        redisService.deleteLoginUser("123456");

        LoginUser getUser = redisService.getLoginUser("123456");
        Assert.assertNull(getUser);
    }

    @Test
    public void testSearchLoginUser() {
        // 添加测试数据
        LoginUser user1 = new LoginUser("192.168.1.1", "user1");
        LoginUser user2 = new LoginUser("192.168.1.2", "user2");
        redisService.setLoginUser("123456", user1, 60, TimeUnit.SECONDS);
        redisService.setLoginUser("789012", user2, 60, TimeUnit.SECONDS);

        // 搜索测试
        Page<String> page = redisService.searchLoginUser("user*", PageRequest.of(0, 10));
        Assert.assertEquals(page.getTotalElements(), 2);
        Assert.assertEquals(page.getContent().size(), 2);
        Assert.assertTrue(page.getContent().contains("123456"));
        Assert.assertTrue(page.getContent().contains("789012"));
    }
}

原文地址:https://blog.csdn.net/qq_22783587/article/details/134446850

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_33278.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注