Spring Data MongoDB坐标转换异常求助:查询/插入附近用户失败
咱们先拆解下你遇到的问题:抛出ConverterNotFoundException是因为Spring Data MongoDB找不到合适的转换器,把org.springframework.data.geo.Point转换成MongoDB需要的两种地理空间格式——要么是[lon, lat]的数组,要么是带type的GeoJSON Document结构。加上你的实体类字段和插入/查询逻辑没对应好,才导致了这个异常。下面一步步来解决:
1. 调整User实体类的字段映射(最推荐的方案)
Spring Data MongoDB提供了GeoJsonPoint类,专门用来映射MongoDB的GeoJSON Point类型,它会自动序列化成{"type":"Point", "coordinates":[...]}的结构,不需要额外转换器。对于数组类型的location字段,我们可以手动把Point转成Double[]数组。
修改后的实体类示例:
import org.springframework.data.mongodb.core.geo.GeoJsonPoint; import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.geo.Point; @Document(collection = "places") public class User { private String userID; // 对应MongoDB中的location数组字段:[经度, 纬度] private Double[] location; // 对应MongoDB中的latlong GeoJSON Point字段 private GeoJsonPoint latlong; // 自定义方法,用普通Point同时给两个字段赋值 public void setPoint(Point point) { this.location = new Double[]{point.getX(), point.getY()}; this.latlong = new GeoJsonPoint(point.getX(), point.getY()); } // Getter和Setter方法 public String getUserID() { return userID; } public void setUserID(String userID) { this.userID = userID; } public Double[] getLocation() { return location; } public void setLocation(Double[] location) { this.location = location; } public GeoJsonPoint getLatlong() { return latlong; } public void setLatlong(GeoJsonPoint latlong) { this.latlong = latlong; } }
2. 修复插入和查询逻辑
现在实体类字段和MongoDB结构匹配了,插入和查询就不会报错了:
插入操作
org.springframework.data.geo.Point point = new Point(12.885970,77.656180); User u = new User(); u.setUserID("123"); u.setPoint(point); mongoTemplate.insert(u, "places");
查询操作
针对GeoJSON类型的latlong字段,用nearSphere方法更适合球面距离计算(对应2dsphere索引);数组类型的location字段可以用near:
// 查询latlong字段(GeoJSON Point) Query mongoQuery1 = new Query(); mongoQuery1.addCriteria(Criteria.where("latlong").nearSphere(new GeoJsonPoint(point.getX(), point.getY()))); List<User> nearbyUsersByGeoJson = mongoTemplate.find(mongoQuery1, User.class, "places"); // 查询location字段(数组类型) Query mongoQuery2 = new Query(); mongoQuery2.addCriteria(Criteria.where("location").near(point)); List<User> nearbyUsersByArray = mongoTemplate.find(mongoQuery2, User.class, "places");
3. (可选)如果坚持用普通Point+自定义转换器
如果你不想用GeoJsonPoint,可以自定义转换器来实现Point到Document和Double[]的转换:
编写转换器类
import org.springframework.core.convert.converter.Converter; import org.springframework.data.geo.Point; import org.bson.Document; // 将Point转成GeoJSON Point的Document public class PointToGeoJsonConverter implements Converter<Point, Document> { @Override public Document convert(Point source) { return new Document("type", "Point") .append("coordinates", new double[]{source.getX(), source.getY()}); } } // 将Point转成Double数组 public class PointToDoubleArrayConverter implements Converter<Point, Double[]> { @Override public Double[] convert(Point source) { return new Double[]{source.getX(), source.getY()}; } }
注册转换器到Spring配置
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.mongodb.core.convert.MongoCustomConversions; import java.util.ArrayList; import java.util.List; @Configuration public class MongoConfig { @Bean public MongoCustomConversions mongoCustomConversions() { List<Converter<?, ?>> converters = new ArrayList<>(); converters.add(new PointToGeoJsonConverter()); converters.add(new PointToDoubleArrayConverter()); return new MongoCustomConversions(converters); } }
之后你的User类就可以直接用Document类型定义latlong字段,Double[]定义location字段,插入时直接赋值Point即可。
4. 确认2dsphere索引正确
确保你针对两个字段都创建了2dsphere索引(如果需要球面距离查询):
import org.springframework.data.mongodb.core.index.GeospatialIndex; import org.springframework.data.mongodb.core.index.GeospatialIndexType; // 给latlong字段创建2dsphere索引 mongoTemplate.indexOps("places").ensureIndex(new GeospatialIndex("latlong").typed(GeospatialIndexType.GEO_2DSPHERE)); // 给location字段创建2dsphere索引 mongoTemplate.indexOps("places").ensureIndex(new GeospatialIndex("location").typed(GeospatialIndexType.GEO_2DSPHERE));
这样调整后,之前的转换异常和查询问题应该都能解决了。
内容的提问来源于stack exchange,提问作者nithin

