如何通过Spring Data Redis Repository一次性获取多个哈希?
你遇到的这个问题很典型——Spring Data Redis的CrudRepository默认不支持通过方法名推导In类型的查询(也就是你写的getByIdIn方法),这是因为Redis的哈希结构本身没有原生的批量获取多个哈希键值对的命令,所以方法推导会直接报错。
下面给你几个更优雅的解决方案,顺便回答你关于HashMapper传入executePipelined的问题:
方案一:用RedisKeyValueTemplate批量查询(最省心)
Spring Data Redis提供了RedisKeyValueTemplate,它已经封装了批量查询的逻辑,还会自动处理实体的key前缀(对应你@RedisHash("Message")的配置)和实体映射,包括LocalDateTime这类特殊类型——只要你配置了支持Java 8时间序列化的Redis序列化器就行。
代码示例:
@Autowired private RedisKeyValueTemplate keyValueTemplate; public Set<Message> getMessagesById(Set<String> ids) { Iterable<Message> messages = keyValueTemplate.find(ids, Message.class); return StreamSupport.stream(messages.spliterator(), false) .collect(Collectors.toSet()); }
这个方法内部其实已经用了Pipeline来批量执行HGETALL命令,而且不需要你手动处理Hash映射,比你自己写Pipeline简洁太多。
方案二:给Repository添加自定义@Query(用Lua脚本)
如果你想坚持用Repository接口,可以通过@Query注解写Lua脚本实现批量查询,Lua脚本可以在Redis端一次性执行多个HGETALL命令,减少网络开销:
@Repository public interface MessageRepository extends CrudRepository<Message, String> { @Query(value = """ local result = {} for _, id in ipairs(ARGV) do -- 这里的前缀要和@RedisHash的配置一致 local hashKey = 'Message:' .. id table.insert(result, redis.call('HGETALL', hashKey)) end return result """, returnType = ReturnType.MULTI) List<Map<String, String>> findByIds(List<String> ids); }
调用这个方法后,你可以用自己的DTOConverter把Map<String, String>转换成Message实体,或者用Spring提供的HashMapper来处理。
方案三:改进你的Pipeline方案,传入HashMapper
关于你问的能不能把mapper传入executePipelined——当然可以!你可以利用Lambda的闭包特性捕获外部的HashMapper,或者更优雅地用executePipelined的重载方法,指定结果转换器:
public Set<Message> getMessagesById(Set<String> ids) { HashMapper<Message, String, String> mapper = new DTOConverter<>(Message.class); List<Message> messages = template.executePipelined( (RedisCallback<Object>) connection -> { StringRedisConnection conn = (StringRedisConnection) connection; ids.stream() .map(id -> "Message:" + id) .forEach(conn::hGetAll); return null; }, // 这里直接指定结果转换器,把每个HGETALL的结果转换成Message result -> result instanceof Map ? mapper.fromHash((Map<String, String>) result) : null ); return new HashSet<>(messages); }
另外,如果你不想自己写DTOConverter,可以用Spring的Jackson2HashMapper,只要给它配置支持Java 8时间类型的ObjectMapper:
ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new JavaTimeModule()); HashMapper<Message, String, String> mapper = new Jackson2HashMapper(objectMapper, false);
这样它就能自动把LocalDateTime的字符串反序列化成对象,不用你手动处理类型转换。
最后补充
建议优先用RedisKeyValueTemplate的方案,它是Spring Data Redis官方推荐的批量查询方式,代码最简洁,也能避免很多手动处理的坑。如果必须用Repository,再考虑Lua脚本的方式。
内容的提问来源于stack exchange,提问作者VB_

