java - 如何使用 Java JDBCPreparedStatement 占位符将 GeoJSON 多边形插入 PostGIS 中?

标签 java jdbc leaflet postgis geojson

如果您使用 javascript 库“Leaflet”及其插件“Draw”,那么您可以检索如下所示的 geojson:

{
    "type" : "Polygon",
    "coordinates" : [ [ [ 13.95938491821289, 53.41329518072117 ], [ 14.955865859985348, 54.412618223375205 ], [ 13.9606294631958, 54.4120067663998 ], [ 13.95938491821289, 53.41329518072117 ] ] ]
}

如果您将上述 json 发送到网络服务器(使用 Java 代码,例如 Play Framework),那么我想知道 在 postgis 数据库中插入或更新它的最简单方法,无需使用 用于创建 sql 字符串的字符串连接。 事实上,它可以创建具有上述结构的 SQL 字符串,但您希望避免字符串连接,这就是我关于如何改为使用PreparedStatement 占位符的问题的原因。 例如,以下 SQL 代码可以直接从 pgAdmin 执行(也可以通过 JDBC 代码):

UPDATE polygons
SET 
    geom = ST_SetSRID(ST_GeomFromGeoJSON
    ('{
            "type" : "Polygon",
            "coordinates" : [ [ [ 13.95938491821289, 53.41329518072117 ], [ 14.955865859985348, 54.412618223375205 ], [ 13.9606294631958, 54.4120067663998 ], [ 13.95938491821289, 53.41329518072117 ] ] ]
    }'), 26918)  
WHERE polygonid = 1;

但是,正如大多数开发人员已经了解到的那样,我们应该避免字符串连接,而是 使用占位符,例如如果使用PreparedStatement(或者例如如果使用Spring框架则使用JdbcTemplate)。

下面是一些代码来说明什么有效,什么无效。

我正在使用的 PostGIS 数据库表是使用以下语句创建的:

CREATE TABLE polygons (
  polygonid serial NOT NULL,
  geom geometry(Polygon,26918),
  CONSTRAINT polygon_pkey PRIMARY KEY (polygonid)
);

在我的 Maven 文件 pom.xml 中,我使用此依赖项:

<dependency>
    <groupId>net.postgis</groupId>
    <artifactId>postgis-jdbc</artifactId>
    <version>2.2.0</version>
</dependency>

Java代码:

import java.sql.*;
import org.postgis.PGgeometry;
...
// For verbosity reasons, I am below not including 
// any try/catch/throw statements or any closings of connections/statements/recordsets

String url = "jdbc:postgresql://localhost:5432/nameOfYourDatabase";
Connection connection = DriverManager.getConnection(url, "postgres", "[YOUR_PASSWORD]");    

// First below is some SQL query code which works and thus 
// proves that the setup with jar files and database connection indeed works:    
long maxPolygonId  = 100L;
PreparedStatement ps = connection.prepareStatement(
    "SELECT polygonid , geom FROM polygons WHERE polygonid < ?"
);
ps.setLong(1, maxPolygonId);
ResultSet rs = ps.executeQuery();
while(rs.next()) {
    long polygonId = rs.getLong(1);
    // Actually it works to use PGgeometry as below WITHOUT first having to do 
    // "((org.postgresql.PGConnection)conn).addDataType("geometry",Class.forName("org.postgis.PGgeometry"));" 
    // as currently documented at http://postgis.net/docs/ch06.html#idp48666800
    PGgeometry geom = (PGgeometry)rs.getObject(2); 
    System.out.println("polygonId: " + polygonId);
    System.out.println("geom: " + geom); // e.g. "geom: SRID=26918;POLYGON((17.9661226272583 59.41375375704178, ... ,17.9661226272583 59.41375375704178))"
}        

// Below is some code that also indeed works fine but it uses string 
// concatenation for the json string instead of placeholder which I want to use instead   
String geoJson = 
    "   {   " +
    "       \"type\" : \"Polygon\",  " +
    "       \"coordinates\" : [ [ [ 13.95938491821289, 53.41329518072117 ], [ 14.955865859985348, 54.412618223375205 ], [ 13.9606294631958, 54.4120067663998 ], [ 13.95938491821289, 53.41329518072117 ] ] ]  " +
    "   }  ";                   
String sqlUpdatingWithGeoJson = 
        "  UPDATE polygons  " +
        "   SET  " +
        "       geom = ST_SetSRID(ST_GeomFromGeoJSON  " +
        "       ('  " +
                    geoJson + 
        "       '), 26918)  " +
        "   WHERE polygonid = ?  ";
long idForPolygonToUpdate = 1L;
ps = connection.prepareStatement(sqlUpdatingWithGeoJson);
ps.setLong(1, idForPolygonToUpdate);
ps.executeUpdate();

// Below is some code which does NOT work when I am trying 
// to use placeholder instead of string concatenation as above 
String sqlUpdatingWithGeoJsonAsPlaceHolder = 
        "  UPDATE polygons  " +
        "   SET  " +
        "       geom = ST_SetSRID(ST_GeomFromGeoJSON  " +
        "       ('  " +
                    " ? " + // NOTE that this line is the difference with the sql update string above ! 
        "       '), 26918)  " +
        "   WHERE polygonid = ?  ";         

ps = connection.prepareStatement(sqlUpdatingWithGeoJsonAsPlaceHolder);
ps.setString(1, geoJson);
ps.setLong(2, idForPolygonToUpdate);
// The above row results in the exception below
// org.postgresql.util.PSQLException: The column index is out of range: 2, number of columns: 1.            
ps.executeUpdate();               


// Below I instead tried  to replace the whole expression as a string to be set 
// through the placeholder but it does NOT work neither  
String geomValue =
        "       ST_SetSRID(ST_GeomFromGeoJSON  " +
        "       ('  " +
                    geoJson + 
        "       '), 26918)  ";          
String sqlUpdatingWithPostGisStatementForPlaceHolder = 
        "  UPDATE polygons  " +
        "   SET  " +
        "       geom = ?  " +
        "   WHERE polygonid = ?  ";
ps = connection.prepareStatement(sqlUpdatingWithPostGisStatementForPlaceHolder);
ps.setString(1, geomValue); // I have also tried setObject instead of setString here
ps.setLong(2, idForPolygonToUpdate);
// The above row also results in the same exception as previous above i.e. below exception:
// org.postgresql.util.PSQLException: The column index is out of range: 2, number of columns: 1.    
ps.executeUpdate();

也许解决方案是将 json 字符串转换为一些可以理解的 Java 对象 通过 PostGIS/PostgreSQL JDBC 驱动程序,然后使用方法“prepareStatement.setObject”, 但是该 Java 对象应该使用什么类型以及如何轻松地将 json 字符串转换为这样的对象?

最佳答案

String sqlUpdatingWithGeoJsonAsPlaceHolder = 
        "  UPDATE polygons  " +
        "   SET  " +
        "       geom = ST_SetSRID(ST_GeomFromGeoJSON(?), 26918)  " +
        "   WHERE polygonid = ?  ";         

ps = connection.prepareStatement(sqlUpdatingWithGeoJsonAsPlaceHolder);
ps.setString(1, geoJson);
ps.setLong(2, idForPolygonToUpdate);      
ps.executeUpdate();   

这个应该可以工作:您将 geojson 作为字符串传递。函数 ST_GeomFromGeoJSON 需要一个字符串作为第一个参数,因此可以准备并执行该语句。

您收到该错误是因为您引用了问号。这样,问号就不会被解释为占位符!

关于java - 如何使用 Java JDBCPreparedStatement 占位符将 GeoJSON 多边形插入 PostGIS 中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36664739/

相关文章:

java - appfuse:我应该配置 jdbc 凭据两次吗?

php - 将 Bing map 图 block 与 Leaflet 一起使用

java - Dozer 在 getClassMap 上抛出 NullPointerException

java - fireTableStructureChanged 后如何在 JTable 中维护用户指定的表行排序?

java - 提取 GWT 服务的结果

java - Tomcat找不到直接放在classes文件夹下的类

java - 如何使用 JDBC 在 Oracle 中插入很长的时间(以毫秒为单位)

java - JDBC 未选择数据库

leaflet - 查明是否已将传单控件添加到 map 中

angularjs - Angular Mapbox vs Angular Leaflet Mapbox vs Angular 谷歌地图