Spring Data Redis按地理空间坐标查询位置报错,如何正确实现?
解决Spring Data Redis Geo查询的ConverterNotFoundException问题
这个问题我之前也碰到过,本质是两个核心问题:你用了错误的仓库查询方法命名(Redis的Geo结构不支持精确坐标匹配查询),以及手动配置的RedisTemplate缺少处理Point类型的序列化/反序列化转换器。下面给你一步步解决:
1. 修正仓库接口的Geo查询方法
Spring Data Redis针对@GeoIndexed标注的字段,提供了专门的Geo查询方法命名规则,不能直接用findByCoordinates(Point)来做精确匹配(Redis的Geo存储是基于空间索引的,不是键值对的精确匹配)。你需要改用以下两种常用的Geo查询方法:
import org.springframework.data.geo.Circle; import org.springframework.data.geo.Distance; import org.springframework.data.geo.Point; import org.springframework.data.repository.CrudRepository; import java.util.List; public interface PositionRepository extends CrudRepository<Position, String> { // 查询指定坐标点附近、指定距离内的位置 List<Position> findByCoordinatesNear(Point point, Distance distance); // 查询指定圆形区域内的位置(Circle包含中心点和半径距离) List<Position> findByCoordinatesWithin(Circle circle); }
2. 修复RedisTemplate的序列化配置
你手动配置的RedisTemplate没有设置能处理Point类型的序列化器,导致Spring无法将Point转换为Redis能识别的字节数组。可以通过配置Jackson序列化器并注册Spring Data Redis的模块来解决:
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.geo.Point; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.data.redis.serializer.json.SpringDataRedisModule; import org.springframework.data.redis.serializer.json.PointDeserializer; import org.springframework.data.redis.serializer.json.PointSerializer; @Configuration @EnableRedisRepositories(basePackages = "com.yourpackage.repository") // 记得替换成你的仓库包路径 public class RedisConfig { @Bean public RedisConnectionFactory lettuceConnectionFactory() { return new LettuceConnectionFactory("localhost", 6379); } @Bean public RedisTemplate<String, Position> redisTemplate() { RedisTemplate<String, Position> template = new RedisTemplate<>(); template.setConnectionFactory(lettuceConnectionFactory()); // 配置Jackson序列化器,支持Point等Geo类型 Jackson2JsonRedisSerializer<Position> valueSerializer = new Jackson2JsonRedisSerializer<>(Position.class); ObjectMapper objectMapper = new ObjectMapper(); // 注册Spring Data Redis的模块,包含Geo类型的序列化支持 objectMapper.registerModule(new SpringDataRedisModule()); // 也可以手动注册Point的序列化/反序列化器(可选,SpringDataRedisModule已经包含) objectMapper.registerModule(new SimpleModule() .addSerializer(Point.class, new PointSerializer()) .addDeserializer(Point.class, new PointDeserializer())); valueSerializer.setObjectMapper(objectMapper); // 设置各类序列化器 template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(valueSerializer); template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(valueSerializer); template.afterPropertiesSet(); return template; } }
3. 调用示例
现在你可以正常调用Geo查询方法了,比如查询距离北京天安门(116.40, 39.90)10公里内的位置:
import org.springframework.data.geo.Distance; import org.springframework.data.geo.Metrics; import org.springframework.data.geo.Point; import org.springframework.beans.factory.annotation.Autowired; import java.util.List; // 在你的服务类中 public class PositionService { @Autowired private PositionRepository positionRepository; public List<Position> getNearbyPositions() { // 中心点坐标 Point centerPoint = new Point(116.40, 39.90); // 10公里的距离范围 Distance distance = new Distance(10, Metrics.KILOMETERS); // 调用查询方法 return positionRepository.findByCoordinatesNear(centerPoint, distance); // 或者用Circle查询,效果一样 // Circle circle = new Circle(centerPoint, distance); // return positionRepository.findByCoordinatesWithin(circle); } }
额外说明
如果你用的是Spring Boot项目,其实可以不用手动配置RedisTemplate——Spring Boot会自动配置支持Geo类型的RedisTemplate,你只需要添加@EnableRedisRepositories注解即可,这样能避免很多序列化配置的坑。
内容的提问来源于stack exchange,提问作者y.luis.rojo




