You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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

火山引擎 最新活动