具有几何返回类型的 Spring 数据存储库和本机查询

Posted

技术标签:

【中文标题】具有几何返回类型的 Spring 数据存储库和本机查询【英文标题】:Spring Data repository and native query with Geometry return type 【发布时间】:2019-02-19 04:28:54 【问题描述】:

我正在创建一个使用一些空间查询的项目。 我使用带有 Spring 数据存储库的 Spring Boot 和带有 PostGIS 扩展的 PostgreSQL 作为数据库。

我创建了这个存储库:

import com.vividsolutions.jts.geom.Geometry;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

@Repository
public interface AreaRepository extends CrudRepository<Area, Long> 

    /*
      extra queries for Area here
    */

    @Query(value="select st_intersection(" +
                ":base_layer ," +
                ":filter_layer" +
                ")", nativeQuery = true)
    Geometry geometryIntersectGeometry(@Param("base_layer") Geometry baseGeometry,@Param("filter_layer") Geometry filterGeometry);


它包含对 Area 实体的一些查询。我还想使用一些 PostGIS 函数来做一些计算,所以我创建了 geometryIntersectGeometry 来从 PostGis 调用 st_intersection 函数,这应该返回一个几何。

我在设置中将hibernate方言设置为PostGIS:

spring.datasource.driverClassName=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/test_db
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.jpa.properties.hibernate.dialect = org.hibernate.spatial.dialect.postgis.PostgisDialect

我有休眠空间的依赖项:

        <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>$hibernate.version</version>
        </dependency>
        ...

调用geometryIntersectGeometry函数会报错:

No Dialect mapping for JDBC type: 1111; nested exception is org.hibernate.MappingException: No Dialect mapping for JDBC type: 1111,

如何告诉 JPA/Spring Data 将几何(PostGIS 类型)响应映射到 Geometry(com.vividsolutions.jts.geom.Geometry) 对象?

【问题讨论】:

【参考方案1】:

通过编写存储库的 custom implementation 并注册类型来修复它(谢谢 Simon Martinelli)

存储库:

import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

@Repository
public interface AreaRepository extends CrudRepository<Area, Long>, AreaGisRepository 

    /*
      extra queries for Area here
    */


界面:

import com.vividsolutions.jts.geom.Geometry;

public interface AreaGisRepository 

    Geometry geometryIntersectGeometry(Geometry baseGeometry, Geometry filterGeometry);

和实施:

import com.vividsolutions.jts.geom.Geometry;
import org.hibernate.spatial.JTSGeometryType;
import org.hibernate.spatial.dialect.postgis.PGGeometryTypeDescriptor;
import org.springframework.beans.factory.annotation.Autowired;

import javax.persistence.EntityManager;

public class AreaGisRepositoryImpl implements AreaGisRepository 

    private EntityManager entityManager;

    @Autowired
    public AreaGisRepositoryImpl(EntityManager entityManager) 
        this.entityManager = entityManager;
    

    @Override
    public Geometry geometryIntersectGeometry(Geometry baseGeometry, Geometry filterGeometry) 
        return (Geometry) entityManager
                .createNativeQuery(
                        "select st_intersection(:base_layer , :filter_layer) as geom")
                .setParameter("base_layer", baseGeometry)
                .setParameter("filter_layer", filterGeometry)
                .unwrap(org.hibernate.query.NativeQuery.class)
                .addScalar("geom", new JTSGeometryType(PGGeometryTypeDescriptor.INSTANCE))
                .getSingleResult();
    


它工作得很好,但是我现在对 Postgis 有一个硬编码的依赖(我们不太可能会使用其他东西,但是...)

【讨论】:

【参考方案2】:

你添加了 Hibernate Spatial 吗?

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

这支持 GIS 数据:http://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#spatial

【讨论】:

是的,我知道,我有依赖关系。抱歉会在问题中添加依赖项。 看看如何在Hibernate Dialect中注册类型vladmihalcea.com/hibernate-no-dialect-mapping-for-jdbc-type【参考方案3】:

我认为有一种更简单的方法可以完成这项工作。 Hibernate Spatial 注册了许多用于 HQL/JQL 的空间函数。所以以下应该工作

@Query(value="select intersection(" +
            ":base_layer ," +
            ":filter_layer" +
            ")")
Geometry geometryIntersectGeometry(@Param("base_layer") Geometry baseGeometry,@Param("filter_layer") Geometry filterGeometry);

请参阅documentation 了解空间方言中可用的功能列表。

【讨论】:

嗨 Karel,当我尝试这样做时,它会引发异常: 原因:org.hibernate.QueryException:节点没有数据类型:org.hibernate.hql.internal.ast.tree.MethodNode \ -[METHOD_CALL] MethodNode: '(' +-[METHOD_NAME] IdentNode: 'intersection' originalText=intersection \-[EXPR_LIST] SqlNode: 'exprList' +-[NAMED_PARAM] ParameterNode: '?' name=base_layer, expectedType =null \-[NAMED_PARAM] ParameterNode: '?' name=filter_layer, expectedType=null [select intersection(:base_layer ,:filter_layer)] 我自己试了一下,发现了两个问题。第一个是“节点没有数据类型”问题。这可以通过使用如下表达式来解决:'select intersection( cast( :base as geolatte_geometry ), :test)' (转换有助于 Hibernate 解析返回类型)。第二个问题是 HQL/JPQL 需要映射实体。看不到第二个问题的好解决方案。所以你的修复似乎是最好的。

以上是关于具有几何返回类型的 Spring 数据存储库和本机查询的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Spring Boot JPA 在 Postgres 中存储几何点?

Spring 数据存储库和 DAO Java 泛型

Spring JPA findAll 在更改 spring.datasource.url 后返回空,即使使用 SELECT * 的本机查询返回数据

我的 Entity Framework 存储库和服务层方法应返回哪些类型:List、IEnumerable、IQueryable?

如何通过 Spring Boot JPA 执行具有 INTERVAL 子句的本机 SQL 查询?

带有 Spring JPA 的 H2 中的几何数据类型