【问题标题】:Map a PostGIS geometry point field with Hibernate on Spring Boot在 Spring Boot 上使用 Hibernate 映射 PostGIS 几何点字段
【发布时间】:2015-02-21 20:46:42
【问题描述】:

在我的 PostgreSQL 9.3 + PostGIS 2.1.5 中,我有一个表 PLACE,其中有一列 coordinates 类型为 Geometry(Point,26910)

我想将它映射到我的 Spring Boot 1.1.9 Web 应用程序中的 Place 实体,该应用程序使用 Hibernate 4.0.0 + 。 Place 可用于 REST 存储库。

不幸的是,当我GET http://localhost:8080/mywebapp/places 收到这个奇怪的 JSON 响应

{

  "_embedded" : {

    "venues" : [ {

      "id" : 1,

      "coordinates" : {

        "envelope" : {

          "envelope" : {

            "envelope" : {

              "envelope" : {

                "envelope" : {

                  "envelope" : {

                    "envelope" : {

                      "envelope" : {

                        "envelope" : {

                          "envelope" : {

                            "envelope" : {

                              "envelope" : {

                                "envelope" : {

                                  "envelope" : {

                                    "envelope" : {

                                      "envelope" : {

                                        "envelope" : {

                                          "envelope" : {

                                            "envelope" : {

等等不确定...!春季日志没有帮助..

我正在使用这个 application.properties:

spring.jpa.database-platform=org.hibernate.spatial.dialect.postgis.PostgisDialect
spring.jpa.show-sql=false
spring.jpa.hibernate.ddl-auto=update

spring.datasource.url=jdbc:postgresql://192.168.1.123/mywebapp
spring.datasource.username=postgres
spring.datasource.password=mypwd
spring.datasource.driverClassName=org.postgresql.Driver

首先,用database-platform代替database可以吗? 也许我必须使用以下设置而不是上述设置?

spring.datasource.url=jdbc:postgresql_postGIS://192.168.1.123/mywebapp
spring.datasource.driverClassName=org.postgis.DriverWrapper

反正我的实体是这样的:

@Entity
public class Place {
    @Id
    public int id;
    @Column(columnDefinition="Geometry")
    @Type(type="org.hibernate.spatial.GeometryType")    //"org.hibernatespatial.GeometryUserType" seems to be for older versions of Hibernate Spatial
    public com.vividsolutions.jts.geom.Point coordinates;
}

我的 pom.xml 包含这个相关部分:

<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>9.3-1102-jdbc41</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-spatial</artifactId>
    <version>4.3</version><!-- compatible with Hibernate 4.3.x -->
    <exclusions>
        <exclusion>
            <artifactId>postgresql</artifactId>
            <groupId>postgresql</groupId>
        </exclusion>
    </exclusions>
</dependency>

有点奇怪的配置,我在网上找到的,目前最好用的一个。

我希望有人能帮助我解开这个谜团。 :)

【问题讨论】:

    标签: spring hibernate postgresql spring-boot postgis


    【解决方案1】:

    该问题似乎与 PostgreSQL 无关。您的 POJO 似乎有一个反向引用,这意味着您的映射器不知道如何处理它。您需要明确定义递归关系,以便映射器知道何时停止。 (我的转到链接 --> http://vard-lokkur.blogspot.com/2010/10/json-jackson-to-rescue.html

    【讨论】:

    • 没有参考任何其他实体,这不是问题。还是谢谢你!
    【解决方案2】:

    最后我发现我的配置没问题,可能是 Jackson 无法正确管理Point 数据类型。于是我定制了它的JSON序列化和反序列化:

    • 将这些注释添加到我们的coordinates 字段中:

      @JsonSerialize(using = PointToJsonSerializer.class)
      @JsonDeserialize(using = JsonToPointDeserializer.class)
      
    • 创建这样的序列化器:

      import java.io.IOException;
      import com.fasterxml.jackson.core.JsonGenerator;
      import com.fasterxml.jackson.core.JsonProcessingException;
      import com.fasterxml.jackson.databind.JsonSerializer;
      import com.fasterxml.jackson.databind.SerializerProvider;
      import com.vividsolutions.jts.geom.Point;
      
      public class PointToJsonSerializer extends JsonSerializer<Point> {
      
          @Override
          public void serialize(Point value, JsonGenerator jgen,
                  SerializerProvider provider) throws IOException,
                  JsonProcessingException {
      
              String jsonValue = "null";
              try
              {
                  if(value != null) {             
                      double lat = value.getY();
                      double lon = value.getX();
                      jsonValue = String.format("POINT (%s %s)", lat, lon);
                  }
              }
              catch(Exception e) {}
      
              jgen.writeString(jsonValue);
          }
      
      }
      
    • 创建这样的反序列化器:

      import java.io.IOException;
      import com.fasterxml.jackson.core.JsonParser;
      import com.fasterxml.jackson.core.JsonProcessingException;
      import com.fasterxml.jackson.databind.DeserializationContext;
      import com.fasterxml.jackson.databind.JsonDeserializer;
      import com.vividsolutions.jts.geom.Coordinate;
      import com.vividsolutions.jts.geom.GeometryFactory;
      import com.vividsolutions.jts.geom.Point;
      import com.vividsolutions.jts.geom.PrecisionModel;
      
      public class JsonToPointDeserializer extends JsonDeserializer<Point> {
      
          private final static GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(), 26910); 
      
          @Override
          public Point deserialize(JsonParser jp, DeserializationContext ctxt)
                  throws IOException, JsonProcessingException {
      
              try {
                  String text = jp.getText();
                  if(text == null || text.length() <= 0)
                      return null;
      
                  String[] coordinates = text.replaceFirst("POINT ?\\(", "").replaceFirst("\\)", "").split(" ");
                  double lat = Double.parseDouble(coordinates[0]);
                  double lon = Double.parseDouble(coordinates[1]);
      
                  Point point = geometryFactory.createPoint(new Coordinate(lat, lon));
                  return point;
              }
              catch(Exception e){
                  return null;
              }
          }
      
      }
      

    也许您也可以使用this serializerthis deserializer,可用here

    【讨论】:

    • 我注意到你的序列化器的输出纬度和经度被交换了。这是预期的吗?此代码解释它double lat = value.getY();double lon = value.getX(); 谢谢
    • @randytan 我很久以前就使用过这段代码,但我记得纬度和经度的顺序是正确的。你在哪里看到他们交换了?
    • 如果您发送POINT(A B) 并执行sys.out 到您的变量jsonValue = String.format("POINT (%s %s)", lat, lon);,您可以看到它实际上已写入POINT(B A)。我只是交换代码value.getY()value.getX() 来更正位置。谢谢
    • @bluish 我想知道如何将其作为 POLYGON 序列化器和反序列化器。
    【解决方案3】:

    这个序列化/反序列化对我来说也很好用。

    https://github.com/bedatadriven/jackson-datatype-jts

    【讨论】:

      【解决方案4】:

      上述解决方案帮助我解决了问题。我简化了它以便其他人可以理解。

      我在我的 pom.xml 中包含了这个库:

      <dependency>
        <groupId>com.bedatadriven</groupId>
        <artifactId>jackson-datatype-jts</artifactId>
        <version>2.2</version>
      </dependency>
      

      这是我使用的 POJO 对象。然后,我能够在没有信封错误和正确坐标的情况下让 REST 调用工作。

      import com.bedatadriven.jackson.datatype.jts.serialization.GeometryDeserializer;
      import com.bedatadriven.jackson.datatype.jts.serialization.GeometrySerializer;
      import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
      import com.fasterxml.jackson.databind.annotation.JsonSerialize;
      import com.vividsolutions.jts.geom.Geometry;
      
      @Entity
      @Table(name = "boundary")
      public class Boundary {
      
          private int id;
          private Geometry geometry;
      
          @Id
          public int getId() {
              return ogc_fid;
          }
      
          public void setId(int id) {
              this.id = id;
          }
          
          @JsonSerialize(using = GeometrySerializer.class)
          @JsonDeserialize(using = GeometryDeserializer.class)
          @Column(name = "geometry", columnDefinition = "Geometry")
          public Geometry getGeometry() {
              return geometry;
          }
      
          public void setGeometry(Geometry geometry) {
              this.geometry = geometry;
          }
      }
      

      我的表有这两列:

      id       | integer            
      geometry | geometry(Geometry,4326) | 
      

      【讨论】:

      • 拯救了我的一天 :)
      • @Martin Naughton 如果我使用@JsonDeserialize(using = JsonDeserialize.class),它会返回此错误。 incompatible types found java.lang.class com.fasterxml.jackson.databind.annotation.jsondeserialize required: java.lang.Class&lt;? extends com.fasterxml.jackson.databind.JsonDeserializer&lt;?&gt;&gt;如何使用解串器?
      【解决方案5】:

      如果您不想在所有使用 Point 的字段上添加注释,您还可以使用 @JsonComponent 注册您的 JsonSerializer 和 JsonDeserializer.

      @JsonComponent
      public class PointSerializer extends JsonSerializer<com.vividsolutions.jts.geom.Point>{
      
          @Override
       public void serialize(com.vividsolutions.jts.geom.Point value, JsonGenerator gen, SerializerProvider provider) throws IOException {
           gen.writeStartObject();
           gen.writeNumberField("lat", value.getY());
           gen.writeNumberField("lon", value.getX());
           gen.writeEndObject();
       }
      }
      

      【讨论】:

        【解决方案6】:

        使用 Hibernate 5.4 和 Posgis 13+,它变得非常容易。我们可以添加最新的hibernate-spatial依赖

            <!-- Hibernate Spatial for storing and retrieving geometries -->
            <dependency>
                <groupId>org.hibernate</groupId>
                <artifactId>hibernate-spatial</artifactId>
                <version>${hibernate.version}</version>
            </dependency>
        

        使用latest Posgis dialect。添加application.properties

        spring.jpa.properties.hibernate.dialect=org.hibernate.spatial.dialect.postgis.PostgisPG95Dialect
        

        然后我们可以简单地使用 JST 几何,因为它受 Hibernate 支持(请参阅here)。

        import org.locationtech.jts.geom.Point;
        
        @Entity(name = "address")
        public class Address {
         private Point location;
        }
        

        SQL 创建实体表:

        CREATE TABLE if not EXISTS address (location geometry);
        

        进一步的 JTS GeoJSON 读取器和写入器可用于将几何图形转换为 JSON(请参阅here

        【讨论】:

          猜你喜欢
          • 2016-08-13
          • 2016-03-03
          • 1970-01-01
          • 1970-01-01
          • 2018-07-11
          • 2013-11-07
          • 1970-01-01
          • 2014-10-13
          • 1970-01-01
          相关资源
          最近更新 更多