Mysql空間データストレージを例として、スキャフォールディングmybatisカスタムフィールドタイプを最初から構築および開発します

序文

最近、5 km以内の関心のあるポイント(たとえば、周囲5 km以内のトイレ)に同様のリターンを達成するために、緯度と経度の情報を保存する必要があるプロジェクトに対する需要があります。技術的な習熟度と運用および保守のコストを考慮に入れます。 、選択は次のとおりです。

  • 永続層フレームワーク:mybatis-plus

  • データベース:Mysql5.7.x

一般的な認識

データベース

データベースは、経度:、lng緯度:の2つのフィールドを作成しますlat

  • lng:フィールドタイプdecimal(9、6)

  • lat:フィールドタイプdecimal(9、6)

コード

class toilet {
    
    
    String name;
    double lng;
    double lat
}

ここでは詳しく説明しません。これは非常に一般的な方法です。

これは空間データ構造ではないため、空間インデックスを作成できず、クエリのパフォーマンスにボトルネックが発生する可能性があります。

カスタムタイプ+空間データタイプ

MySQLは空間データ型をサポートしています。

空間データ型と関数を使用することができMyISAMInnoDBNDB、およびARCHIVEテーブル。空間列のインデックスを作成するために使用され、MyISAMおよびInnoDBサポートの両方SPATIALと非SPATIALインデックス。他のストレージエンジンは非SPATIALインデックスをサポートします

MySQLの基盤となるストレージ形式

mysqlの公式ウェブサイトをクエリすると、結果は次のようになります

Geometryの実際のストレージ形式は次のとおりです。長さ25バイト

  • 整数SRIDの場合は4バイト(0)
  • 1バイト(整数バイト順)(1 =リトルエンディアン)
  • 整数型情報の4つのバイトは、(MySQLは示すために1〜7の値を使用してPointLineStringPolygonMultiPointMultiLineStringMultiPolygon、およびGeometryCollection。)
  • 8バイトの倍精度X座標
  • 8バイトの倍精度Y座標

たとえばPOINT(1 -1)、次の25バイトのシーケンス構成され、各シーケンスは2つの16進数で表さます。

mysql> SET @g = ST_GeomFromText('POINT(1 -1)');
mysql> SELECT LENGTH(@g);
+------------+
| LENGTH(@g) |
+------------+
|         25 |
+------------+
mysql> SELECT HEX(@g);
+----------------------------------------------------+
| HEX(@g)                                            |
+----------------------------------------------------+
| 000000000101000000000000000000F03F000000000000F0BF |
+----------------------------------------------------+
組成 サイズ
SRID 4バイト 00000000
バイトオーダー 1バイト 01
WKBタイプ 4バイト 01000000
X座標 8バイト 000000000000F03F
Y座標 8バイト 000000000000F0BF

データベース

データベースにフィールドを作成しますcoordinate

  • 座標:フィールドタイプポイント

コード

エンティティクラス

class toilet {
    
    
    String name;
    
	geopoint location;
}
// 自定义数据类型
class geopoint {
    
    
    double lng;
    double lat 
}

GeoPointTypeHandler

@Slf4j
@MappedTypes({
    
    GeoPoint.class})
public class GeoPointTypeHandler extends BaseTypeHandler<GeoPoint> {
    
    
    /**
     * 空间参照标识系 MySQL数据库默认为0
     */
    private static int SRID = 0;
    /**
     * 字节顺序指示符为1或0表示小端或大端存储。小字节序和大字节序分别也称为网络数据表示(NDR)和外部数据表示(XDR)
     */
    private static byte ENDIAN = (byte) 1;


    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, GeoPoint parameter, JdbcType jdbcType) throws SQLException {
    
    
        ps.setBytes(i, to(parameter));
    }

    @Override
    public GeoPoint getNullableResult(ResultSet rs, String columnName) throws SQLException {
    
    
        return parse(rs.getBytes(columnName));
    }

    @Override
    public GeoPoint getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
    
    
        return parse(rs.getBytes(columnIndex));
    }

    @Override
    public GeoPoint getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
    
    
        return parse(cs.getBytes(columnIndex));
    }

    /**
     * bytes转GeoPoint对象
     *
     * @param bytes
     */
    private GeoPoint parse(byte[] bytes) {
    
    
        ByteBuffer wrap = ByteBuffer.wrap(bytes)
                // 小端点排序(Java默认是大端点排序,这里要改下)
                .order(ByteOrder.LITTLE_ENDIAN);
        int SRID = wrap.getInt();
        byte endian = wrap.get();
        int wkbType = wrap.getInt();
        double x = wrap.getDouble();
        double y = wrap.getDouble();
        GeoPoint geoPoint = new GeoPoint(x, y);
        log.info("geo-point:{}", JSONUtil.toJsonStr(geoPoint));
        return geoPoint;
    }

    /**
     * GeoPoint转bytes对象
     *
     * @param geoPoint
     */
    private byte[] to(GeoPoint geoPoint) {
    
    
        ByteBuffer wrap = ByteBuffer.allocate(25)
                // 小端点排序(Java默认是大端点排序,这里要改下)
                .order(ByteOrder.LITTLE_ENDIAN);
        // SRID: 0
        wrap.putInt(SRID);
        // 字节顺序指示符为1或0表示小端或大端存储。小字节序和大字节序分别也称为网络数据表示(NDR)和外部数据表示(XDR)
        wrap.put(ENDIAN);
        // WKB类型是指示几何类型的代码 wkbType: 1 MySQL使用从1个值至7,以表示 Point,LineString, Polygon,MultiPoint, MultiLineString, MultiPolygon,和 GeometryCollection。
        wrap.putInt(1);
        // X坐标
        wrap.putDouble(geoPoint.getLon());
        // Y坐标
        wrap.putDouble(geoPoint.getLat());
        return wrap.array();
    }
}

エンティティクラスと関連ソリューション

@TableName(autoResultMap = true) // 注意!! 必须开启映射注解
class toilet {
    
    
    String name;
    @TableField(typeHandler = GeoPointTypeHandler.class)
	geopoint location;
}

テスト

        Toilet toilet = new Toilet();
        toilet.setName("laker");
        toilet.setLocation(new GeoPoint(123.23, 1.2)); // 直接塞实体
        toiletService.save(toilet);
		// 查询
		List<Toilet> toilets = toiletService.list();
	...
        [{
    
    "name":"laker",location:{
    
    "lng":123.23,"lat":1.2}}]

距離を計算する

計算結果は、AutoNaviが提供する計算距離APIと一致しています。
Gaodeは距離webapiを計算します

SELECT ( st_distance_sphere ( point ( 116.481028,39.989643  ), point ( 114.465302,40.004717 ), 6378137.0 ) ) AS distance

参照:

  • https://dev.mysql.com/doc/refman/5.7/en/gis-data-formats.html

おすすめ

転載: blog.csdn.net/abu935009066/article/details/114943494