etiquetas - spring boot stereotypes
Asigne un campo de punto de geometrĂa PostGIS con Hibernar en Spring Boot (5)
En mi PostgreSQL 9.3 + PostGIS 2.1.5 tengo un PLACE
tabla con una columna de coordinates
de tipo Geometry(Point,26910)
.
Quiero asignarlo a la entidad Place
en mi aplicación web Spring Boot 1.1.9, que utiliza Hibernate 4.0.0 +. Place
está disponible con un repositorio REST.
Desafortunadamente cuando GET http://localhost:8080/mywebapp/places
, recibo esta extraña respuesta 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" : {
¡Y así indefinidamente ...! El registro de primavera no ayuda ..
Estoy trabajando con esta 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
En primer lugar, ¿está bien usar database-platform
de database
de database
lugar de una database
de database
? ¿Y tal vez tengo que usar la siguiente configuración en lugar de la anterior?
spring.datasource.url=jdbc:postgresql_postGIS://192.168.1.123/mywebapp
spring.datasource.driverClassName=org.postgis.DriverWrapper
De todos modos mi entidad es algo como esto:
@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;
}
Mi pom.xml contiene esta parte relevante:
<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>
Una configuración un poco extraña, la encontré en internet, es la que mejor funciona por ahora.
Espero que alguien me pueda ayudar con este misterio. :)
El problema no parece estar relacionado con PostgreSQL. Parece que su POJO tiene una referencia inversa, lo que significa que su asignador no sabe cómo manejarlo. Debe definir explícitamente las relaciones recursivas para que el asignador sepa cuándo detenerse. (Mi enlace de Goto -> http://vard-lokkur.blogspot.com/2010/10/json-jackson-to-rescue.html )
Esta serialización / deserialización también funcionó bien para mí.
Finalmente, descubrí que mi configuración está bien y podría ser Jackson que no puede administrar el tipo de datos de Point
correctamente. Así que personalicé su serialización y deserialización JSON:
agregue estas anotaciones a nuestro campo de
coordinates
:@JsonSerialize(using = PointToJsonSerializer.class) @JsonDeserialize(using = JsonToPointDeserializer.class)
crear tal serializador:
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); } }
crear tal deserializador:
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; } } }
Quizás también pueda usar este serializador y este deserializador , disponibles here .
Las soluciones anteriores me ayudaron a solucionar el problema. Lo simplifico para que otras personas puedan entenderlo.
Incluí esta biblioteca en mi pom.xml:
<dependency>
<groupId>com.bedatadriven</groupId>
<artifactId>jackson-datatype-jts</artifactId>
<version>2.2</version>
</dependency>
Este es el objeto POJO que usé. Luego pude hacer que la llamada REST funcione sin el error de la envolvente y los datos adecuados.
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 geomertry;
@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 getGeomertry() {
return geomertry;
}
public void setGeomertry(Geometry geomertry) {
this.geomertry = geomertry;
}
}
Mi mesa tenía estas 2 columnas:
id | integer
geometry | geometry(Geometry,4326) |
Si no desea agregar la anotación en todos los campos que usan un Punto, también puede usar @JsonComponent para registrar su JsonSerializer y 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();
}
}