冬眠空间 6 迁移中的 PostGIS 函数

PostGIS Functions in hibernate spatial 6 migration

提问人:Urman Ratneshwar 提问时间:5/16/2023 最后编辑:Brian ClozelUrman Ratneshwar 更新时间:9/8/2023 访问量:379

问:

第一期:我有一个为休眠空间 5 编写的现有代码,用于查找给定半径内的记录,这很好用。我正在迁移代码hibernate-spatial 6.1.7.Final。但是得到以下错误:

java.lang.IllegalArgumentException: Passed `invariantType` for function return cannot be null
    at org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers.invariant(StandardFunctionReturnTypeResolvers.java:45) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.sqm.internal.SqmCriteriaNodeBuilder.function(SqmCriteriaNodeBuilder.java:1495) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.sqm.internal.SqmCriteriaNodeBuilder.function(SqmCriteriaNodeBuilder.java:153) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at com.adani.amm.specification.AssetLocationSpecification.toPredicate(AssetLocationSpecification.java:35) ~[classes/:na]

根据我到目前为止的调试,该版本似乎不支持 POSTGIS 地理功能。 下面是我的代码片段。

public class AssetLocationSpecification implements Specification<Location> {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private final Double radius;
    private final Double latitude;
    private final Double longitude;

    public AssetLocationSpecification(Double radius, Double latitude, Double longitude) {
        super();
        this.radius = radius;
        this.latitude = latitude;
        this.longitude = longitude;
    }

    @Override
    public Predicate toPredicate(Root<Location> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
        Expression<Geometry> geography = criteriaBuilder.function("geography", Geometry.class, root.get("geometry"));
        Expression<Point> point = criteriaBuilder.function("ST_Point", Point.class, criteriaBuilder.literal(longitude),
                criteriaBuilder.literal(latitude));
        Expression<Point> centerPoint = criteriaBuilder.function("ST_SetSRID", Point.class, point,
                criteriaBuilder.literal(4326));
        Expression<Boolean> expression = criteriaBuilder.function(SpatialFunction.dwithin.toString(), boolean.class,
                geography, centerPoint, criteriaBuilder.literal(radius));
        return criteriaBuilder.equal(expression, true);
    }
}

第二个问题:org.hibernate.spatial.SpatialFunction.dwithin 已弃用,替换枚举 (org.hibernate.spatial.CommonSpatialFunction) 不包含该函数。

spring-boot 空间休眠- 6.x

评论

0赞 adarshr 8/14/2023
你找到这个问题的解决方案了吗?

答:

0赞 Enrico 9/8/2023 #1

我在执行完全相同的迁移时在我的一个项目中遇到了同样的错误。我调试了产生错误的 Hibernate Spatial 代码,据我了解,Hibernate 类型的注册方式在此版本的 Hibernate Spatial 中发生了变化,并且在第一个语句中用作返回类型的类型在已注册的基本类型列表中找不到。我不能更具体,因为我也没有完全理解机制。Geometry

说完这句话后,这个错误在我看来太奇怪了,让我考虑这个错误是否不在我这边。我想也许我以一种“不常见”的方式使用空间 APi,我试图重构我的代码,试图变得更加通用,而不直接使用 PostGIS 函数。

我的原始代码是这样的:

public static Specification<Station> withinBBox(
        final double lon1,final double lat1,
        final double lon2, final double lat2) {
    return new Specification<Station>() {
        public Predicate toPredicate(Root<Station> root, CriteriaQuery<?> query,
                                     final CriteriaBuilder builder) {
            Expression<Geometry> bBoxExpression = builder.function(
                    "ST_MakeEnvelope",
                    Geometry.class,
                    builder.literal(lon1),
                    builder.literal(lat1),
                    builder.literal(lon2),
                    builder.literal(lat2),
                    builder.literal(4326));
            return builder.isTrue(
                    builder.function(
                            "ST_Within",
                            Boolean.class,
                            root.<Point>get("geoLocation"),
                            bBoxExpression)
            );
        }
    };
}

我重构为:

public static Specification<Station> withinBBox(
        final double lon1,final double lat1,
        final double lon2, final double lat2) {
    return new Specification<Station>() {
        public Predicate toPredicate(Root<Station> root, CriteriaQuery<?> query,
                                     final CriteriaBuilder builder) {
            if(builder instanceof SqmCriteriaNodeBuilder) {
                JTSSpatialCriteriaBuilder jtsBuilder = ((SqmCriteriaNodeBuilder) builder).unwrap(JTSSpatialCriteriaBuilder.class);
                Expression<? extends Geometry> bBoxExpression = jtsBuilder.literal(GEOMETRY_FACTORY.createPolygon(new Coordinate[] {
                    new Coordinate(lon1, lat1),
                    new Coordinate(lon1, lat2),
                    new Coordinate(lon2, lat2),
                    new Coordinate(lon2, lat1),
                    new Coordinate(lon1, lat1)
                }));
                Expression<? extends Geometry> stationLocation = root.<Point>get("geoLocation");
                return jtsBuilder.isTrue(jtsBuilder.within(stationLocation, bBoxExpression));
            }
            return builder.isTrue(builder.literal(false));
        }
    };
}

你可以看到我不再使用 PostGIS 来创建对象,而是使用我以这种方式创建的 GeometryFactory:Geometrynew GeometryFactory(new PrecisionModel(), 4326);

这种方法与数据库无关。

将相同的方法应用于您的案例,您可以尝试编写如下内容:

JTSSpatialCriteriaBuilder jtsBuilder = ((SqmCriteriaNodeBuilder) builder).unwrap(JTSSpatialCriteriaBuilder.class);
GeometryFactory gf = new GeometryFactory(new PrecisionModel(), 4326);
Expression<? extends Geometry> geography = jtsBuilder.literal(root.get("geometry"));
Expression<? extends Geometry> centerPoint = jtsBuilder.literal(gf.createPoint(new Coordinate(longitude, latitude)));
//This is not needed, the geometryfactory already works with SRID 4326
//Expression<Point> centerPoint = criteriaBuilder.function("ST_SetSRID", Point.class, point, criteriaBuilder.literal(4326)); 
Expression<Boolean> isWithin = jtsBuilder.distanceWithin(geography, centerPoint, radius);