做地图和LBS业务11年了,今天不整虚的,直接说mybatis geo这玩意儿到底怎么搞才不坑。很多兄弟一上来就搞空间索引,结果查询慢得像蜗牛,或者坐标算出来差之千里。这篇只讲干货,解决你项目里最头疼的空间查询慢、精度丢失、配置繁琐这三大痛点。
先说个真事。去年有个客户,做个同城配送系统,用MySQL默认的geometry类型,没建空间索引。高峰期查附近5公里商家,接口直接超时。为啥?因为全表扫描啊,兄弟。那时候我就知道,光会写SQL不行,得懂底层逻辑。
MyBatis本身不直接处理地理数据,它只是个ORM框架。真正干活的是Hibernate Spatial或者JTS Topology Suite。你得在pom里把依赖加对。别像我当年那样,导错了包,调试三天三夜,头发都掉了一把。
第一步,实体类映射。别直接用String存经纬度,太Low。要用Point类型。比如:
private Point location;
然后在MyBatis的ResultMap里,得配好TypeHandler。这点很多人忽略,导致查出来是null或者乱码。一定要指定org.hibernate.spatial.type.GeometryTypeHandler。
第二步,查询语句怎么写。别用普通的=号。要用ST_Within或者ST_Distance_Sphere。
SELECT * FROM shop WHERE ST_Within(location, ST_GeomFromText('POLYGON(...)'));
注意,这里的坐标系很重要。如果是高德地图,用的是GCJ-02,MySQL默认是WGS84。直接查会有偏差,大概几百米。你得在入库前做坐标转换,或者在查询时动态转换。这一步很关键,不然用户搜到的店,实际距离差了半个城市。
再说性能。空间索引不是万能的。你得确保你的geometry字段加了SPATIAL INDEX。
ALTER TABLE shop ADD SPATIAL INDEX idx_location (location);
加了索引后,查询速度提升不止一个量级。但是,别滥用。如果数据量小,比如几万条,普通索引+范围过滤可能更快。空间索引适合百万级以上数据。我有个项目,百万级门店数据,加了索引后,响应时间从2秒降到50毫秒。这差距,老板都看得到的。
还有个小坑。MyBatis Plus用户注意,如果你用了MP,得自定义TypeHandler。MP默认的TypeHandler不支持Geometry类型。你得写个类继承BaseTypeHandler,重写setNonNullParameter和getNullableResult方法。
public class GeometryTypeHandler extends BaseTypeHandler
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Point parameter, JdbcType jdbcType) throws SQLException {
// 转换逻辑
}
// ...
}
注册到MyBatis配置里。别偷懒,网上抄的代码不一定适合你的版本。
最后,调试技巧。别光看日志。用MySQL的EXPLAIN PLAN看看有没有用到空间索引。如果type是ALL,说明索引没生效。检查你的SQL写法,是不是用了函数包裹了字段,比如ST_Distance(location, ?),这样会导致索引失效。要把常量放在函数里,字段放外面。
ST_Distance(ST_GeomFromText('POINT(...)'), location) < 5000
这样写,索引才能用上。
总之,mybatis geo这块,细节决定成败。坐标转换、索引优化、TypeHandler配置,每一步都得抠清楚。别指望一蹴而就,多测几次,多看看执行计划。
希望这些经验能帮你少走弯路。如果有具体问题,欢迎留言,我尽量回。毕竟,踩过的坑,不想让你们再踩一遍。
本文关键词:mybatis geo