使用MyBatis类型转换器将PostGIS中的几何字段转为PostGIS/GeoTools的几何Java对象

一 概述

在实际GIS项目开发中,经常会使用PostGIS来存储矢量数据,在PostGIS中建立几何字段存储空间几何信息,对应于Java服务端,我们希望对应的表实体(MyBatis)中直接采用几何对象来进行数据操作,比如采用org.postgis.Geometry或者org.locationtech.jts.geom.Geometry(GeoTools)等JavaType来声明属性,以便于我们直接进行几何的数据操作,比如进行几何合并、求交、测距等等。但是这两种JavaType并非JdbcType,无法直接映射,需要我们利用MyBatis的类型转换器进行自定义转换处理。
关于MyBatis类型转换器的介绍与配置,可以参考我的另一篇文章 《MyBatis 类型转换器》

二 转PostGIS在Java中组织的几何Java对象

(1)简述

PostGIS在Java中组织的几何有关的JavaType位于 org.postgis.**,具有Geometry(几何抽象类)及其实现类。由PostGIS组织的几何JavaType更接近PostGIS的JdbcType。
image.png
PostGIS的JdbcType,例如org.postgis.PGgeometry,内部包含由对应的JavaType的几何对象。
image.png

(2)类型转换器

首先定义一个对org.postgis.PGgeometry类型的自定义类型转换器的抽象类,后续其余具体几何类型的转换只需要实现此抽象类指定类型即可。

public abstract class PgGeometryTypeHandler<T extends Geometry> extends BaseTypeHandler<T> {
    /**
     * SRS-EPSG
     */
    private static final int EPSG_CODE = 4326;

    /**
     * 插入 - 插入时设置参数类型
     *
     * @param preparedStatement
     * @param i
     * @param parameter         org.postgis组织的几何类型实体
     * @param jdbcType
     * @throws SQLException
     */
    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, T parameter, JdbcType jdbcType) throws SQLException {
        boolean typeCheck = parameter instanceof Geometry;
        if (!typeCheck) {
            preparedStatement.setObject(i, null);
            System.out.println("MyBatis类型转换器插入参数非org.postgis.Geometry[Point,LineString,Polygon,MultiPoint,MultiLineString,MultiPolygon]");
        }

        PGgeometry pGgeometry = new PGgeometry(parameter);
        parameter.setSrid(EPSG_CODE);
        preparedStatement.setObject(i, pGgeometry);
    }

    /**
     * 获取 - 获取时转换回的自定义类型 - 根据列名获取
     *
     * @param resultSet
     * @param columnName
     * @return
     * @throws SQLException
     */
    @Override
    public T getNullableResult(ResultSet resultSet, String columnName) throws SQLException {
        // 获取读取到的指定列存储的值并进行类型检查
        Object result = resultSet.getObject(columnName);
        boolean typeCheck = result instanceof PGgeometry;
        if (!typeCheck) {
            System.out.println("MyBatis类型转换器获取参数转自定义类型错误,列实际存储类型非org.postgis.PGgeometry");
            return null;
        }
        return (T) ((PGgeometry) result).getGeometry();
    }

    /**
     * 获取 - 获取时转换回的自定义类型 - 根据索引位置获取
     *
     * @param resultSet
     * @param columnIndex
     * @return
     * @throws SQLException
     */
    @Override
    public T getNullableResult(ResultSet resultSet, int columnIndex) throws SQLException {
        // 获取读取到的指定列存储的值并进行类型检查
        Object result = resultSet.getObject(columnIndex);
        boolean typeCheck = result instanceof PGgeometry;
        if (!typeCheck) {
            System.out.println("MyBatis类型转换器获取参数转自定义类型错误,列实际存储类型非org.postgis.PGgeometry");
            return null;
        }
        return (T) ((PGgeometry) result).getGeometry();
    }

    /**
     * 获取 - 获取时转换回的自定义类型 - 根据存储过程获取
     *
     * @param callableStatement
     * @param columnIndex
     * @return
     * @throws SQLException
     */
    @Override
    public T getNullableResult(CallableStatement callableStatement, int columnIndex) throws SQLException {
        // 获取读取到的指定列存储的值并进行类型检查
        Object result = callableStatement.getObject(columnIndex);
        boolean typeCheck = result instanceof PGgeometry;
        if (!typeCheck) {
            System.out.println("MyBatis类型转换器获取参数转自定义类型错误,列实际存储类型非org.postgis.PGgeometry");
            return null;
        }
        return (T) ((PGgeometry) result).getGeometry();
    }
}
/**
 * mybatis 几何字段类型转换处理器 - PostGIS - Point
 */
@MappedTypes({Point.class})
public class PgPointTypeHandler extends PgGeometryTypeHandler<Point> {
}

三 转GeoTools的几何Java对象

(1)简述

GeoTools组织的几何类型相比PostGIS组织的几何类型要更加“沉重”,前者提供了更多的功能,后者则更接近数据库,效率更高。之所以同时列举出两种转换,就是要清楚这个过程,进而根据实际情况选择。
PostGIS -> PGGeometry(org.postgis) -> Geometry(org.postgis) -> WKT ->Geometry(GeoTools)

(2)类型转换器

首先定义一个对org.locationtech.jts.geom.Geometry(GeoTools)类型的自定义类型转换器的抽象类,后续其余具体几何类型的转换只需要实现此抽象类指定类型即可。

/**
 * mybatis 几何字段类型转换处理器 - geometry抽象类,由具体几何类型继承
 *
 * @param <T>
 */
public abstract class GtGeometryTypeHandler<T extends Geometry> extends BaseTypeHandler<T> {

    /**
     * SRS-EPSG
     */
    private static final int EPSG_CODE = 4326;

    /**
     * 插入 - 插入时设置参数类型
     *
     * @param preparedStatement
     * @param i
     * @param parameter         GeoTools组织的几何类型实体
     * @param jdbcType
     * @throws SQLException
     */
    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, T parameter, JdbcType jdbcType) throws SQLException {
        // 获取要插入的自定义类型的(GeoTools的Geometry实体)实体,并进行类型检查
        boolean typeCheck = parameter instanceof Geometry;
        if (!typeCheck) {
            preparedStatement.setObject(i, null);
            System.out.println("MyBatis类型转换器插入参数非GeoTools的Geometry对象");
        }

        // 通过几何的WKT格式字符串构建PG的Geometry
        PGgeometry pGgeometry = new PGgeometry(parameter.toText());
        org.postgis.Geometry geometry = pGgeometry.getGeometry();
        geometry.setSrid(EPSG_CODE);
        preparedStatement.setObject(i, pGgeometry);
    }

    /**
     * 获取 - 获取时转换回的自定义类型 - 根据列名获取
     *
     * @param resultSet
     * @param columnName
     * @return
     * @throws SQLException
     */
    @Override
    public T getNullableResult(ResultSet resultSet, String columnName) throws SQLException {
        // 获取读取到的指定列存储的值并进行类型检查
        Object result = resultSet.getObject(columnName);
        boolean typeCheck = result instanceof PGgeometry;
        if (!typeCheck) {
            System.out.println("MyBatis类型转换器获取参数转自定义类型错误,列实际存储类型非org.postgis.PGgeometry,列实际存储类型");
            return null;
        }

        // 检查并返回目标转换类型实例
        return getResult((PGgeometry) result);
    }

    /**
     * 获取 - 获取时转换回的自定义类型 - 根据索引位置获取
     *
     * @param resultSet
     * @param columnIndex
     * @return
     * @throws SQLException
     */
    @Override
    public T getNullableResult(ResultSet resultSet, int columnIndex) throws SQLException {
        // 获取读取到的指定列存储的值并进行类型检查
        Object result = resultSet.getObject(columnIndex);
        boolean typeCheck = result instanceof PGgeometry;
        if (!typeCheck) {
            System.out.println("MyBatis类型转换器获取参数转自定义类型错误,列实际存储类型非org.postgis.PGgeometry,列实际存储类型");
            return null;
        }

        // 检查并返回目标转换类型实例
        return getResult((PGgeometry) result);
    }

    /**
     * 获取 - 获取时转换回的自定义类型 - 根据存储过程获取
     *
     * @param callableStatement
     * @param columnIndex
     * @return
     * @throws SQLException
     */
    @Override
    public T getNullableResult(CallableStatement callableStatement, int columnIndex) throws SQLException {
        // 获取读取到的指定列存储的值并进行类型检查
        Object result = callableStatement.getObject(columnIndex);
        boolean typeCheck = result instanceof PGgeometry;
        if (!typeCheck) {
            System.out.println("MyBatis类型转换器获取参数转自定义类型错误,列实际存储类型非org.postgis.PGgeometry,列实际存储类型");
            return null;
        }

        // 检查并返回目标转换类型实例
        return getResult((PGgeometry) result);
    }

    /**
     * pgGeometry实例转GeoTools的Geometry实例
     *
     * @param pgGeometry
     * @return
     */
    private T getResult(PGgeometry pgGeometry) {
        if (pgGeometry == null)
            return null;

        // 替换掉pgWKT中关于SRID的字符串部分
        // WKT-PG:"SRID=4326;POINT(118.88888888 36.6666666666)"
        // WKT:"POINT(118.88888888 36.6666666666)"
        String pgWkt = pgGeometry.toString();
        String wkt = pgWkt.replace(String.format("SRID=%s;", EPSG_CODE), "");

        // 通过WKT构建GeoTools的Geometry对象
        try {
            return (T) new WKTReader().read(wkt);
        } catch (Exception e) {
            System.out.println("解析WKT为GeoTools的Geometry对象失败,异常信息:"+ e.toString());
            return null;
        }
    }
}
/**
 * mybatis 几何字段类型转换处理器 - Point
 */
@MappedTypes({Point.class})
public class GtPointTypeHandler extends GtGeometryTypeHandler<Point> {
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值