当前位置: 首页 > news >正文

SpringBoot+MyBatis+MySQL的Point实现范围查找

前言

最近做了一个功能,需要通过用户当前位置点获取指定范围内的数据。由于后端存储用的是 MySQL,故选择使用 MySQL 中的 Point 实现范围查找功能。ORM 框架用的是 MyBatis,MyBatis 原生并不支持 Point 字段与 POJO 的映射,需要自定义 MyBatis 的 TypeHandler 实现该功能。

当然,你可以通过定义两个 MySQL 字段(经度和维度)来代替 Point 也可以实现范围查找,但是既然是使用的 MyBatis,那么还是希望能在 MyBatis 中直接操作 Point,提高代码通用性。

关于 MySQL 的 POINT

在MySQL中,POINT 是一种用于存储地理空间数据的数据类型,它表示二维空间中的一个点。MySQL 从 5.7 版本开始,提供了对地理空间数据类型的原生支持,包括 POINT、LINESTRING、POLYGON 等。

POINT 数据类型用于存储一个二维坐标点,其格式为 (X, Y),其中 X 和 Y 分别表示该点在二维平面上的横坐标和纵坐标。

注意,在用 POINT 存储经纬度时,X 为经度,Y 为纬度,不要弄反了。因为将经纬度存储到 POINT 时并没有循序限制,但是使用 POINT 相关函数时就有限制了。比如ST_Distance_Sphere

组件版本

  • SpringBoot 2.4.3
  • MyBatis-Plus 3.4.2
  • MySQL 8.0.26

建表(含 POINT 字段)

create table group_ride_info
(id             bigint unsigned                            not null comment '主键id'primary key,create_time    datetime                                   not null comment '创建时间',update_time    datetime         default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP comment '修改时间',create_by      int unsigned     default '0'               not null comment '创建人',update_by      int unsigned     default '0'               not null comment '修改人',is_delete      tinyint unsigned default '0'               not null comment '是否删除。默认0,1-是,0-否',...create_point   point                                      not null comment '创建时坐标'
)comment '团信息表';create spatial index create_pointon group_ride_info (create_point);

其中,create_point 是通过 POINT 字段记录的经纬度坐标,POINT 字段建议设置为为空。同时需要给 PIOINT 类型字段创建空间索引。

alter table group_ride_info add SPATIAL index(create_point);

定义 GeoPoint 对象

@Builder
@AllArgsConstructor
@Data
public class GeoPoint implements Serializable {/*** 经度*/private Double longitude;/*** 纬度*/private Double latitude;
}

定义表对象

@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("group_ride_info")
@ApiModel(value="GroupRideInfo对象", description="团信息表")
public class GroupRideInfo implements Serializable {private static final long serialVersionUID = 1L;@ApiModelProperty(value = "主键id")@TableId(value = "id", type = IdType.NONE)private Long id;@ApiModelProperty(value = "创建时间")private LocalDateTime createTime;@ApiModelProperty(value = "修改时间")private LocalDateTime updateTime;@ApiModelProperty(value = "创建人")private Integer createBy;@ApiModelProperty(value = "修改人")private Integer updateBy;@ApiModelProperty(value = "是否删除。默认0,1-是,0-否")private Integer isDelete;...@ApiModelProperty(value = "创建时坐标")private GeoPoint createPoint;}

定义坐标转换器 GeoPointConverter

public class GeoPointConverter {/*** Little endian or Big endian*/private int byteOrder = ByteOrderValues.LITTLE_ENDIAN;/*** Precision model*/private PrecisionModel precisionModel = new PrecisionModel();/*** Coordinate sequence factory*/private CoordinateSequenceFactory coordinateSequenceFactory = CoordinateArraySequenceFactory.instance();/*** Output dimension*/private int outputDimension = 2;/*** Convert byte array containing SRID + WKB Geometry into Geometry object*/public GeoPoint from(byte[] bytes) {if (bytes == null) {return null;}try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes)) {// Read SRIDbyte[] sridBytes = new byte[4];inputStream.read(sridBytes);int srid = ByteOrderValues.getInt(sridBytes, byteOrder);// Prepare Geometry factoryGeometryFactory geometryFactory = new GeometryFactory(precisionModel, srid, coordinateSequenceFactory);// Read GeometryWKBReader wkbReader = new WKBReader(geometryFactory);Geometry geometry = wkbReader.read(new InputStreamInStream(inputStream));Point point = (Point) geometry;// convert to GeoPointGeoPoint geoPoint = new GeoPoint(point.getX(), point.getY());return geoPoint;} catch (IOException | ParseException e) {throw new IllegalArgumentException(e);}}/*** Convert Geometry object into byte array containing SRID + WKB Geometry*/public byte[] to(GeoPoint geoPoint) {if (geoPoint == null) {return null;}Coordinate coordinate = new Coordinate(geoPoint.getLongitude(), geoPoint.getLatitude());CoordinateArraySequence coordinateArraySequence = new CoordinateArraySequence(new Coordinate[]{coordinate}, 2);Point point = new Point(coordinateArraySequence, new GeometryFactory());try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {// Write SRIDbyte[] sridBytes = new byte[4];ByteOrderValues.putInt(point.getSRID(), sridBytes, byteOrder);outputStream.write(sridBytes);// Write GeometryWKBWriter wkbWriter = new WKBWriter(outputDimension, byteOrder);wkbWriter.write(point, new OutputStreamOutStream(outputStream));return outputStream.toByteArray();} catch (IOException ioe) {throw new IllegalArgumentException(ioe);}}
}

定义 GeoPointTypeHandler

@MappedTypes({GeoPoint.class})
public class GeoPointTypeHandler extends BaseTypeHandler<GeoPoint> {GeoPointConverter converter = new GeoPointConverter();@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, GeoPoint parameter, JdbcType jdbcType) throws SQLException {ps.setBytes(i, converter.to(parameter));}@Overridepublic GeoPoint getNullableResult(ResultSet rs, String columnName) throws SQLException {return converter.from(rs.getBytes(columnName));}@Overridepublic GeoPoint getNullableResult(ResultSet rs, int columnIndex) throws SQLException {return converter.from(rs.getBytes(columnIndex));}@Overridepublic GeoPoint getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {return converter.from(cs.getBytes(columnIndex));}
}

配置扫描 TypeHandler

@Configuration
@EnableTransactionManagement
@MapperScan(basePackages = {"${spring.xxx.data.db.basepackage}"}, sqlSessionFactoryRef = "sqlSessionFactoryMasterDb")
@Slf4j
public class DbConfig implements TransactionManagementConfigurer {/*** 注释** @return SqlSessionFactory* @throws Exception 异常*/@Beanpublic SqlSessionFactory sqlSessionFactoryMasterDb() throws Exception {MybatisSqlSessionFactoryBean factoryBean =new MybatisSqlSessionFactoryBean();...// 此处为定义TypeHandler所在的包名factoryBean.setTypeHandlersPackage("com.xxx.module.typehandler");return factoryBean.getObject();}
}

注意,此处代码仅为示例代码,关键代码在factoryBean.setTypeHandlersPackage("com.xxx.module.typehandler");

SpringBoot 项目也可以在配置文件中配置,请自行百度,目的是让自定义 TypeHandler 生效。

MyBatis 使用 POINT

原生 getById()

自定义 SQL(指定范围查找)

  1. Mapper 接口中定义方法
List<GroupRideInfo> getListByPoint2(Integer distance, GeoPoint point, String ticket);
  1. Mapper.xml 中定义查询语句
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.xxx.groupride.domain.po.GroupRideInfo"><id column="id" property="id" /><result column="create_time" property="createTime" /><result column="update_time" property="updateTime" /><result column="create_by" property="createBy" /><result column="update_by" property="updateBy" /><result column="is_delete" property="isDelete" />...<result column="create_point" property="createPoint" />
</resultMap>
<select id="getListByPoint2" resultMap="BaseResultMap">SELECT *,ST_Distance_Sphere(create_point, #{point}) AS distance_metersFROM group_ride_infoHAVING distance_meters &lt; #{distance} and status=1 and ticket=#{ticket}ORDER BY distance_meters asc
</select>
  1. 调用方法

可以看到,可以在 MyBatis 中像普通类型参数一样使用 POINT 了。上面示例仅列举了查询操作,新增/修改也是可以的。

相关文章:

SpringBoot+MyBatis+MySQL的Point实现范围查找

前言 最近做了一个功能&#xff0c;需要通过用户当前位置点获取指定范围内的数据。由于后端存储用的是 MySQL&#xff0c;故选择使用 MySQL 中的 Point 实现范围查找功能。ORM 框架用的是 MyBatis&#xff0c;MyBatis 原生并不支持 Point 字段与 POJO 的映射&#xff0c;需要自…...

【Apache Paimon】-- 1 -- Apache Paimon 是什么?

目录 1、简介 2、概览 3、哪些场景可以使用 Paimon 4、周边生态 5、小结 6、参考 1、简介 我们听说过数据仓库、数据湖、数据湖仓,那你听说过流式数据仓库(Stream warehouse,简称:Streamhouse)吗?那我们今天就来解锁看看他们之中的新秀: Apache paimon 到底是什么…...

解决VsCode无法跳转问题

在settings.json中加入以下代码 { "files.associations": { "*.c":"c", "*.h":"c", "*.s":"masm" }, "includePath":[ "${workspaceFold…...

优化C++设计模式:用模板代替虚函数与多态机制

文章目录 0. 引言1. 模板编程替换虚函数和多态的必要性1.1. MISRA C对类型转换和虚函数的规定1.2. 虚函数与多态问题的影响及如何适应MISRA C要求1.3. 模板编程的优势&#xff1a;替代虚函数和多态机制 2. 设计模式改进2.1. 单例模式的改进与静态局部变量的对比(第二种实现) 2.…...

浪浪云轻量服务器搭建vulfocus网络安全靶场

什么是网络安全靶场 网络安全靶场是一个模拟真实网络环境的训练平台&#xff0c;旨在为网络安全专业人员提供一个安全的环境来测试和提高他们的技能。靶场通常包括各种网络设备、操作系统、应用程序和安全工具&#xff0c;允许用户在其中进行攻击和防御练习。以下是网络安全靶…...

C++builder中的人工智能(23):在现代C++ Windows上轻松录制声音

在这篇文章中&#xff0c;我们将探讨如何在现代C Windows上轻松录制声音。声音以波形和数字形式存在&#xff0c;其音量随时间变化。在C Builder中&#xff0c;使用Windows设备进行录音非常简单。要录制声音&#xff0c;在多设备应用程序中&#xff0c;必须使用FMX.Media.hpp头…...

避免误差!Android 中正确计算时间差的方式

在 Android 开发中&#xff0c;计时和计算时间差异是非常常见的需求&#xff0c;比如记录事件发生的间隔、统计应用启动时间、测量网络请求的响应时间等。在实现这些功能时&#xff0c;我们通常需要一个可靠的时间源来确保计时的准确性。那么为什么 Android 推荐使用 SystemClo…...

unity3d————Resources异步加载

知识点一&#xff1a;Resources异步加载是什么&#xff1f; 在Unity中&#xff0c;资源加载可以分为同步加载和异步加载两种方式。同步加载会在主线程中直接进行&#xff0c;如果加载的资源过大&#xff0c;可能会导致程序卡顿&#xff0c;因为从硬盘读取数据到内存并进行处理…...

YOLOv11改进,YOLOv11添加GnConv递归门控卷积,二次创新C3k2结构

摘要 视觉 Transformer 在多种任务中取得了显著的成功,这得益于基于点积自注意力的新空间建模机制。视觉 Transformer 中的关键因素——即输入自适应、长距离和高阶空间交互——也可以通过卷积框架高效实现。作者提出了递归门控卷积(Recursive Gated Convolution,简称 gnCo…...

如何选择国产化CMS来建设政务网站?

在介绍CMS之前&#xff0c;我们先了解国家为什么要网站为什么要完成国产化改造&#xff1f; 1、信创国产化网站建站响应了国家的信息安全战略&#xff0c;支持自主可控的信息技术产业的发展&#xff0c;减少对进口软硬件的依赖&#xff0c;保障国家信息安全。 2、国产替代&…...

C/C++语言基础--initializer_list表达式、tuple元组、pair对组简介

本专栏目的 更新C/C的基础语法&#xff0c;包括C的一些新特性 前言 initializer_list表达式、tuple元组、pair对组再C日常还是比较常用的&#xff0c;尤其是对组在刷算法还是挺好用的&#xff0c;这里做一个简介&#xff1b;这三个语法结合C17的结构化绑定会更好用&#xff…...

paddle表格识别数据制作

数据格式 其中主要数据有两个一个表格结构的检测框&#xff0c;一个是tokens&#xff0c;注意的地方是 1、只能使用双引号&#xff0c;单引号不行 2、使用带引号的地方是tokens里面 "<tr>", "<td", " colspan2", ">",&quo…...

python selenium库的使用:通过兴趣点获取坐标

通过兴趣点获取坐标 from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.common.exceptions import TimeoutException# 保存Cookies到文件&#xff08;可选&#xff09; import pi…...

如何优化Kafka消费者的性能

要优化 Kafka 消费者性能&#xff0c;你可以考虑以下策略&#xff1a; 并行消费&#xff1a;通过增加消费者组中的消费者数量来并行处理更多的消息&#xff0c;从而提升消费速度。 批量消费&#xff1a;配置 fetch.min.bytes 和 fetch.max.wait.ms 参数来控制批量消费的大小和…...

机器学习 决策树

决策树-分类 1 概念 1、决策节点通过条件判断而进行分支选择的节点。如&#xff1a;将某个样本中的属性值(特征值)与决策节点上的值进行比较&#xff0c;从而判断它的流向。 2、叶子节点没有子节点的节点&#xff0c;表示最终的决策结果。 3、决策树的深度所有节点的最大层…...

效益登记册效益管理计划

效益登记册 benefit Register效益管理计划效益登记册汇集并列出项目集计划的效益&#xff0c;用于在项目集的整个持续时间内测量和沟通效益的交付。在效益识别阶段&#xff0c;效益登记册根据项目集商业论证、组织战略计划和其他相关项目集自标而编制。随后&#xff0c;登记册由…...

Go语言的零值可用性:优势与限制

Go语言以其简洁和高效的设计理念而著称&#xff0c;其中之一便是“零值可用”的特性。这一特性使得许多类型在未显式初始化时即可直接安全地使用&#xff0c;大大简化了代码的初始化过程。然而&#xff0c;并非所有类型都支持零值可用&#xff0c;且在使用时也存在一定的限制。…...

【自用】0-1背包问题与完全背包问题的Java实现

引言 背包问题是计算机科学领域的一个经典优化问题&#xff0c;分为多种类型&#xff0c;其中最常见的是0-1背包问题和完全背包问题。这两种问题的核心在于如何在有限的空间内最大化收益&#xff0c;但它们之间存在一些关键的区别&#xff1a;0-1背包问题允许每个物品只能选择…...

HTML5实现俄罗斯方块小游戏

文章目录 1.设计来源1.1 主界面1.2 皮肤风格1.2 游戏中界面1.3 游戏结束界面 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/143788449 HTML5实现俄罗斯方块小游戏&#x…...

Mybatis官方生成器使用示例

在这篇文章中&#xff0c;我们将通过实际代码示例来说明如何使用 MyBatis Generator (MBG) 来自动化生成 MyBatis 项目所需的实体类、Mapper 接口和 Mapper XML 文件。我们将使用一个 Maven 插件来执行代码生成&#xff0c;并提供详细的配置和解释。 1. MyBatis Generator 简介…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎&#xff08;Physics Engine&#xff09; 物理引擎 是一种通过计算机模拟物理规律&#xff08;如力学、碰撞、重力、流体动力学等&#xff09;的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互&#xff0c;广泛应用于 游戏开发、动画制作、虚…...

STM32+rt-thread判断是否联网

一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

1688商品列表API与其他数据源的对接思路

将1688商品列表API与其他数据源对接时&#xff0c;需结合业务场景设计数据流转链路&#xff0c;重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点&#xff1a; 一、核心对接场景与目标 商品数据同步 场景&#xff1a;将1688商品信息…...

Java 加密常用的各种算法及其选择

在数字化时代&#xff0c;数据安全至关重要&#xff0c;Java 作为广泛应用的编程语言&#xff0c;提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景&#xff0c;有助于开发者在不同的业务需求中做出正确的选择。​ 一、对称加密算法…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

python执行测试用例,allure报乱码且未成功生成报告

allure执行测试用例时显示乱码&#xff1a;‘allure’ &#xfffd;&#xfffd;&#xfffd;&#xfffd;&#xfffd;ڲ&#xfffd;&#xfffd;&#xfffd;&#xfffd;ⲿ&#xfffd;&#xfffd;&#xfffd;Ҳ&#xfffd;&#xfffd;&#xfffd;ǿ&#xfffd;&am…...

CSS设置元素的宽度根据其内容自动调整

width: fit-content 是 CSS 中的一个属性值&#xff0c;用于设置元素的宽度根据其内容自动调整&#xff0c;确保宽度刚好容纳内容而不会超出。 效果对比 默认情况&#xff08;width: auto&#xff09;&#xff1a; 块级元素&#xff08;如 <div>&#xff09;会占满父容器…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...