mysql mybatisPlus 存储经纬度

创建索引

设置字段默认值

复制代码
ALTER TABLE jszj_love_pavilion 
ALTER COLUMN wgs SET DEFAULT (ST_GeomFromText('POINT(0 0)', 4326));

引入依赖

复制代码
 <!-- JTS几何库引入数据库Point类型的支持 -->
        <dependency>
            <groupId>org.locationtech.jts</groupId>
            <artifactId>jts-core</artifactId>
            <version>1.19.0</version>
        </dependency>

在实体类新建字段

复制代码
//wgs84坐标系位置
    @TableField(value = "wgs", typeHandler = JtsPointTypeHandler.class)
    private Point wgs;
复制代码
JtsPointTypeHandler.java
复制代码
package com.mmwzcloud.jszj.framework.typehandler;

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKBReader;
import org.locationtech.jts.io.WKTReader;
import org.locationtech.jts.io.WKTWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.sql.*;

@MappedTypes(Point.class)
@MappedJdbcTypes({
        JdbcType.VARCHAR,    // WKT文本格式
        JdbcType.BINARY,     // WKB二进制格式
        JdbcType.BLOB,       // Blob格式
        JdbcType.LONGVARBINARY
})
public class JtsPointTypeHandler extends BaseTypeHandler<Point> {

    private static final Logger log = LoggerFactory.getLogger(JtsPointTypeHandler.class);

    // WKB格式常量
    private static final int WKB_POINT_TYPE = 1;
    private static final int WKB_SRID_FLAG = 0x20000000;
    private static final byte WKB_NDR = 1;  // 小端字节序
    private static final byte WKB_XDR = 0;  // 大端字节序

    // 几何工厂(线程安全,可共享)
    private static final GeometryFactory GEOMETRY_FACTORY =
            new GeometryFactory(new PrecisionModel(PrecisionModel.FLOATING), 4326);

    // 读写器(非线程安全,需要ThreadLocal或每次创建)
    private static final ThreadLocal<WKTReader> WKT_READER = ThreadLocal.withInitial(
            () -> new WKTReader(GEOMETRY_FACTORY)
    );
    private static final ThreadLocal<WKBReader> WKB_READER = ThreadLocal.withInitial(
            () -> new WKBReader(GEOMETRY_FACTORY)
    );

    // ==================== 写入数据库 ====================

    @Override
    public void setNonNullParameter(PreparedStatement ps, int parameterIndex,
                                    Point point, JdbcType jdbcType) throws SQLException {

        try {

            double x = point.getX();  // 经度
            double y = point.getY();  // 纬度
            int srid = point.getSRID() > 0 ? point.getSRID() : 4326;

            byte[] standardWkb = createStandardWkb(x, y, srid);
            ps.setBytes(parameterIndex, standardWkb);


        } catch (Exception e) {
            log.error("写入Point数据失败", e);
            throw new SQLException("写入几何数据失败: " + e.getMessage(), e);
        }
    }

    public static byte[] createStandardWkb(double lon, double lat, int srid) {
        // 1. 标准 WKB 核心 21 B
        ByteBuffer core = ByteBuffer.allocate(21).order(ByteOrder.LITTLE_ENDIAN);
        core.put((byte) 1);      // little-endian
        core.putInt(1);          // WKB_POINT
        core.putDouble(lon);     // X
        core.putDouble(lat);     // Y
        byte[] wkbCore = core.array();

        // 2. 前面拼 4 B SRID
        return ByteBuffer.allocate(4 + wkbCore.length)
                .order(ByteOrder.LITTLE_ENDIAN)
                .putInt(srid)
                .put(wkbCore)
                .array();
    }

        // ==================== 从数据库读取 ====================

    @Override
    public Point getNullableResult(ResultSet rs, String columnName)
            throws SQLException {
        return parseDatabaseValue(rs.getObject(columnName));
    }

    @Override
    public Point getNullableResult(ResultSet rs, int columnIndex)
            throws SQLException {
        return parseDatabaseValue(rs.getObject(columnIndex));
    }

    @Override
    public Point getNullableResult(CallableStatement cs, int columnIndex)
            throws SQLException {
        return parseDatabaseValue(cs.getObject(columnIndex));
    }

    /**
     * 智能解析数据库返回值
     * 支持: byte[](WKB), String(WKT), Blob, 其他类型自动转换
     */
    private Point parseDatabaseValue(Object dbValue) throws SQLException {
        if (dbValue == null) {
            return null;
        }

        try {
            // 1. byte[] - WKB格式(最常见)
            if (dbValue instanceof byte[]) {
                return parseWkbByByteArray((byte[]) dbValue);
            }

            //mysql返回String类型数据
            if (dbValue instanceof String) {
                return null;//未实现
            }

            return null;
        } catch (Exception e) {
            throw new SQLException("解析几何数据失败", e);
        }
    }

    private Point parseWkbByByteArray(byte[] dbValue) {
        byte[] dbValue1 = (byte[]) dbValue;
        // 打印调试信息
        StringBuilder hex = new StringBuilder();
        for (int i = 0; i < dbValue1.length; i++) {
            hex.append(String.format("%02X ", dbValue1[i] & 0xFF));
        }
        log.debug("原始数据(hex): {}", hex.toString());

        // 字节 9-16: 经度 (double)
        long lngBits = 0;
        // MySQL是小端字节序,但需要反向读取
        for (int i = 16; i >= 9; i--) {
            lngBits = (lngBits << 8) | (dbValue1[i] & 0xFF);
        }
        double longitude = Double.longBitsToDouble(lngBits);

        // 字节 17-24: 纬度 (double)
        long latBits = 0;
        for (int i = 24; i >= 17; i--) {
            latBits = (latBits << 8) | (dbValue1[i] & 0xFF);
        }
        double latitude = Double.longBitsToDouble(latBits);

        log.debug("方法1解析结果: 经度={}, 纬度={}", longitude, latitude);

        GeometryFactory factory = new GeometryFactory(new PrecisionModel(), 4326);
        Point point = factory.createPoint(new Coordinate(longitude, latitude));
        return point;
    }
}
复制代码
CoordinateConverter.java
复制代码
package com.mmwzcloud.jszj.framework.commonjszj.utils;

public class CoordinateConverter {
    private static final double PI = 3.1415926535897932384626;
    private static final double A = 6378245.0;  // 长半轴
    private static final double EE = 0.00669342162296594323;  // 扁率

    /**
     * GCJ-02 转 WGS-84
     * @param lng 高德经度
     * @param lat 高德纬度
     * @return [经度, 纬度]
     */
    public static double[] gcj02ToWgs84(double lng, double lat) {
        if (outOfChina(lng, lat)) {
            return new double[]{lng, lat};
        }

        double dLat = transformLat(lng - 105.0, lat - 35.0);
        double dLng = transformLng(lng - 105.0, lat - 35.0);
        double radLat = lat / 180.0 * PI;
        double magic = Math.sin(radLat);
        magic = 1 - EE * magic * magic;
        double sqrtMagic = Math.sqrt(magic);
        dLat = (dLat * 180.0) / ((A * (1 - EE)) / (magic * sqrtMagic) * PI);
        dLng = (dLng * 180.0) / (A / sqrtMagic * Math.cos(radLat) * PI);

        return new double[]{lng - dLng, lat - dLat};
    }

    /**
     * WGS-84 转 GCJ-02(返回给前端用)
     */
    public static double[] wgs84ToGcj02(double lng, double lat) {
        if (outOfChina(lng, lat)) {
            return new double[]{lng, lat};
        }

        double dLat = transformLat(lng - 105.0, lat - 35.0);
        double dLng = transformLng(lng - 105.0, lat - 35.0);
        double radLat = lat / 180.0 * PI;
        double magic = Math.sin(radLat);
        magic = 1 - EE * magic * magic;
        double sqrtMagic = Math.sqrt(magic);
        dLat = (dLat * 180.0) / ((A * (1 - EE)) / (magic * sqrtMagic) * PI);
        dLng = (dLng * 180.0) / (A / sqrtMagic * Math.cos(radLat) * PI);

        return new double[]{lng + dLng, lat + dLat};
    }

    private static double transformLat(double x, double y) {
        double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(y * PI) + 40.0 * Math.sin(y / 3.0 * PI)) * 2.0 / 3.0;
        ret += (160.0 * Math.sin(y / 12.0 * PI) + 320 * Math.sin(y * PI / 30.0)) * 2.0 / 3.0;
        return ret;
    }

    private static double transformLng(double x, double y) {
        double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(x * PI) + 40.0 * Math.sin(x / 3.0 * PI)) * 2.0 / 3.0;
        ret += (150.0 * Math.sin(x / 12.0 * PI) + 300.0 * Math.sin(x / 30.0 * PI)) * 2.0 / 3.0;
        return ret;
    }

    private static boolean outOfChina(double lng, double lat) {
        return lng < 72.004 || lng > 137.8347 || lat < 0.8293 || lat > 55.8271;
    }
}

高德gcj02 转 Wgs84 并保存

复制代码
double[] doubles = CoordinateConverter.gcj02ToWgs84(Double.valueOf(updateReqVO.getLongitude()), Double.valueOf(updateReqVO.getLatitude()));
        Point wgs84Point = CreateWgs84Point.createWgs84Point(doubles[0], doubles[1]);
        updateReqVO.setWgs(wgs84Point);

查询时 Wgs84 转 gcj02

复制代码
double[] doubles = CoordinateConverter.wgs84ToGcj02(x, y);
        return StringUtils.join(Arrays.asList(String.valueOf(doubles[0]),String.valueOf(doubles[1])),",");
相关推荐
葡萄城技术团队2 小时前
在 Java 中优化 MySQL 查询以提升性能
java·开发语言·mysql
杀死那个蝈坦2 小时前
短链接生成-基于布隆过滤器和唯一索引
java·数据库·微服务·oracle·rocketmq
winfield8212 小时前
Java 中大量闲置 MySQL 连接的解决方案(从根因到落地)
java·mysql
计算机毕设指导62 小时前
基于微信小程序图像识别的智能垃圾分类系统【源码文末联系】
java·spring boot·mysql·微信小程序·小程序·分类·maven
3824278272 小时前
使用 webdriver-manager配置geckodriver
java·开发语言·数据库·爬虫·python
惜分飞3 小时前
Oracle Recovery Tools 使用说明
数据库·oracle·oracle恢复·替代bbed·oracle恢复工具
如旧呀3 小时前
爬虫小知识
数据库·爬虫·mysql
培根芝士3 小时前
解决DBeaver对PostgresSQL备份数据库时报错
数据库
Hello World呀3 小时前
登录时,redis出现错误
数据库·redis·缓存