Hibernate/Spatial的规范/谓词,用于过滤实体是否在半径范围内。

7
我希望将以下查询重写为一个Predicate/Specification,以便我可以将它们链接在一起。
此查询过滤出特定区域内的所有定义的OptEvent实体。
@Query(value = "SELECT * FROM opt_event WHERE ST_DWithin(cast(opt_event.locationpoint as geography),ST_SetSRID(ST_Point(?2, ?1),4326), 100000);", nativeQuery = true)
    public Set<OptEvent> findAllEventsInRange(double longitude, double latitude);

通常我会写一个类似这样的规范,然后可以后续将它们组合起来。具体取决于是否应用了过滤器。

public static Specification<OptEvent> filterArea(Double longitude, Double latitude) {
        return new Specification<OptEvent>() {
            @Override
            public Predicate toPredicate(Root<OptEvent> root, CriteriaQuery<?> query, CriteriaBuilder builder) {

            SpatialRelateExpression geomExpression = SpatialRestrictions.within("locationpoint ", area);
            //this is the only thing i could find, but i have no idea if its the right path or how i can transform it to the Specification
            // what is area ?

                //Example when i filter for the destination in the OptEvent
                //return (root, query, builder) -> builder.like(builder.upper(root.get("destination")),
                //"%" + destination.toUpperCase() + "%");

            }
        };
    }

关于我的环境的更多信息。 以下是我的pom文件。我使用的是Postgres和Hibernate 5.4.10.Final。

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-spatial</artifactId>
            //Version 5.4.10 FINAL from Hibernate-Core
        </dependency>
        <dependency>
            <groupId>com.vividsolutions</groupId>
            <artifactId>jts</artifactId>
            <version>1.13</version>
        </dependency>
    </dependencies>

我使用 org.locationtech.jts.geom.Point 来表示我的位置点。

@Entity
@JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
public class OptEvent {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String destination;
    private String description;
    private int year;
    private EventSeries eventSeries;
    private LocalDate eventDate;


    @JsonSerialize(using = GeometrySerializer.class)
    @JsonDeserialize(contentUsing = GeometryDeserializer.class)
    private Point locationpoint;

    @OneToMany(mappedBy = "event", cascade = CascadeType.ALL, orphanRemoval = true)
    @JsonIgnoreProperties("event")
    private Set<OptResult> results = new HashSet<>();

    public OptEvent() {

    }
1个回答

6

我现在已经自己找到了答案。

public static Specification<Event> filterWithinRadius(double longitute, double latitude, double radius) {
    return new Specification<Event>() {
        @Override
        public Predicate toPredicate(Root<Event> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
            GeometryFactory factory = new GeometryFactory();
            Point comparisonPoint = factory.createPoint(new Coordinate(latitude, longitute));
            return SpatialPredicates.distanceWithin(builder, root.get("location"), comparisonPoint, radius);
        }
    };
}

Important SpatialPredicates在版本5.4.10 FINAL中首次缺失,因此我不得不将依赖项升级到5.4.14 FINAL。

<dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-spatial</artifactId>
        <version>5.4.14.Final</version>
    </dependency>

为了将函数1对1地重建为最初问题中的形式,以下结构是正确的。否则,从几何到地理的转换会丢失。
public static Specification<Event> filterWithinRadius(double longitute, double latitude, double radius) {
    return new Specification<Event>() {
        @Override
        public Predicate toPredicate(Root<Event> root, CriteriaQuery<?> query, CriteriaBuilder builder) {

            Expression<Geometry> geography = builder.function("geography", Geometry.class, root.get("location"));
            Expression<Point> point = builder.function("ST_Point", Point.class, builder.literal(longitute),
                    builder.literal(latitude));
            Expression<Point> comparisonPoint = builder.function("ST_SetSRID", Point.class, point,
                    builder.literal(4326));
            Expression<Boolean> expression = builder.function(SpatialFunction.dwithin.toString(), boolean.class,
                    geography, comparisonPoint, builder.literal(radius));
            return builder.equal(expression, true);
        }
    };
}

1
这是最终解决方案吗?还是你有其他更新的代码? - Ranjithkumar
这是我的最终解决方案。从那以后,我没有在项目上工作过多,因此没有更新。 - dasLicht
我在第一个表达式中遇到了一个错误 - ORA-00904:“GEOGRAPHY”:无效标识符。你能帮忙吗? - Ranjithkumar
我之前不知道有"空间谓词(SpatialPredicates)",太棒了!我一直在使用"CriteriaBuilder"自己构建查询,但这个更好!!谢谢你! - sebasira
什么是“4326”? “半径”以什么单位? - user_x
如果在MySQL中出现错误:“Caused by: java.sql.SQLSyntaxErrorException: FUNCTION db.dwithin does not exist”,该怎么办? - user_x

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接