本文共 5453 字,大约阅读时间需要 18 分钟。
在处理MySQL中的Geometry数据时,WKT(Well-Known Text)和WKB(Well-Known Binary)两种方案各有优劣。本文将从实际使用体验出发,对两种方案进行对比分析。
WKT方案通过将几何数据以文本形式存储(如POINT (1 -1)),在SQL中实现简单易懂的操作。然而,其存在以下问题:
操作复杂性
需要借助ST_GeomFromText
和ST_AsText
等函数,使得SQL语句显得复杂。例如:select id, ST_AsText(geometry) AS geometry, update_time, create_time from geometry_data
类型转换限制
ST_GeomFromText
并非万能的转换函数。对于GeometryCollection
类型,需要使用ST_GeomCollFromText
,这增加了开发的复杂性。特定类型支持不足
对于LinearRing
等特殊类型,无法直接调用现有的转换函数,需要自行实现。WKB(Well-Known Binary)通过二进制存储几何信息,能够有效解决上述问题。WKB的存储结构如下:
0101000000000000000000F03F000000000000F0BF
字节顺序
WKB的字节顺序可以是大顶堆(0)或小顶堆(1)。MySQL默认使用小顶堆存储。类型标识
WKB类型字段对应以下值:坐标信息
存储点、线或面的坐标点。MySQL的Geometry结构实际上是基于WKB存储的,但增加了4个字节用于存储SRID(空间参考系标识符)。因此,WKB是Geometry的一部分,且MySQL默认使用小顶堆存储。
为了实现WKB方案,我们需要自定义TypeHandler。以下是完整的TypeHandler实现:
import org.locationtech.jts.geom.Geometry;import org.locationtech.jts.geom.GeometryFactory;import org.locationtech.jts.geom.PrecisionModel;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import java.sql.Blob;import java.sql.SQLException;/** * WKB类型处理器实现 */@Componentpublic class GeometryTypeWKBHandler extends BaseTypeHandler{ private static final PrecisionModel PRECISION_MODEL = new PrecisionModel(PrecisionModel.FLOATING); @Autowired private GeometryFactory geometryFactory; @Override public Geometry getNullableResult(ResultSet rs, String columnName) throws SQLException { byte[] bytes = rs.getBytes(columnName); return deserializeGeometry(bytes); } @Override public Geometry getNullableResult(ResultSet rs, int columnIndex) throws SQLException { byte[] bytes = rs.getBytes(columnIndex); return deserializeGeometry(bytes); } @Override public Geometry getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { byte[] bytes = cs.getBytes(columnIndex); return deserializeGeometry(bytes); } @Override public void setNonNullParameter(PreparedStatement ps, int i, Geometry parameter) throws SQLException { byte[] bytes = serializeGeometry(parameter); ps.setBytes(i, bytes); } private byte[] serializeGeometry(Geometry geometry) { int srid = geometry.getSRID(); byte[] bytes = new WKBWriter(2, ByteOrderValues.LITTLE_ENDIAN).write(geometry); return ByteBuffer.allocate(bytes.length + 4) .order(ByteOrder.LITTLE_ENDIAN) .putInt(srid) .put(bytes) .array(); } private Geometry deserializeGeometry(byte[] bytes) throws SQLException { if (bytes == null) { return null; } ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); int srid = buffer.getInt(); byte[] geometryBytes = new byte[buffer.remaining()]; buffer.get(geometryBytes); GeometryFactory geometryFactory = geometryFactory; WKBReader reader = new WKBReader(geometryFactory); return reader.read(geometryBytes); }}
在MyBatis Plus配置中,注册自定义TypeHandler:
@Configurationpublic class MyBatisPlusConfig { @Autowired private SqlSessionFactory sqlSessionFactory; @Bean public void registerCustomTypeHandlers() { sqlSessionFactory.getConfiguration().getTypeHandlerRegistry().register( Geometry.class, JdbcType.BLOB, GeometryTypeWKBHandler.class ); }}
CREATE TABLE `t_geo_wkb` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID', `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', `del_flag` char(1) DEFAULT '0' COMMENT '删除标记,0未删除,1已删除', `name` varchar(255) DEFAULT NULL COMMENT '名称', `geo_type` varchar(255) DEFAULT NULL COMMENT 'geo_type', `geo` geometry NOT NULL COMMENT 'geo几何数据-GCJ02', PRIMARY KEY (`id`), SPATIAL KEY `idx_geo` (`geo`) COMMENT '空间数据索引')ENGINE=InnoDB COMMENT='几何数据wkb表';
import org.springframework.beans.annotation.AnnotationConfig;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import java.time.LocalDateTime;import static org.mybatis.mybatisplus.annotations.TableField;@MapperScan("com.charge.ws.core.mapper")public class GeoWkbDO extends BaseDO { private static final long serialVersionUID = 1L; @TableId(value = "id", type = IdType.AUTO) private Long id; @TableField("create_time") private LocalDateTime createTime; @TableField("update_time") private LocalDateTime updateTime; @TableField("del_flag") private String delFlag; @TableField("name") private String name; @TableField("geo_type") private String geoType; @TableField( value = "geo", typeHandler = GeometryTypeWKBHandler.class, jdbcType = JdbcType.BLOB ) private Geometry geo;}
insert into geometry_data(id, geometry, update_time, create_time) values ('#{id}', #{geometry, jdbcType=BLOB, typeHandler=org.example.typehandlers.GeometryTypeWKBHandler}, now(), now())
通过对比WKT和WKB方案,我们发现WKB方案在存储和处理Geometry数据方面具有显著优势。WKB的二进制存储方式能够有效解决传统方案的复杂性问题,同时MySQL的Geometry结构与WKB兼容,通过自定义TypeHandler可以实现高效的数据操作。
转载地址:http://lpdfk.baihongyu.com/