如何使用Java preparedStatement将JSON对象插入Postgres?

时间:2016-03-07 12:46:18

标签: java json postgresql prepared-statement

我正在努力将JSON对象插入到postgres v9.4数据库中。我已经定义了名为" evtjson"的列。作为json类型(不是jsonb)。

我正在尝试在Java(jdk1.8)中使用预准备语句将Json对象(使用JEE javax.json库构建)插入到列中,但我仍然遇到SQLException错误。

我使用:

创建JSON对象
JsonObject mbrLogRec = Json.createObjectBuilder().build();
…
mbrLogRec = Json.createObjectBuilder()
                .add("New MbrID", newId)
                .build();

然后我将此对象作为参数传递给另一个方法,以使用预准备语句将其写入数据库。 (以及其他几个领域)作为:

pStmt.setObject(11, dtlRec);

使用此方法,我收到以下错误:

  

org.postgresql.util.PSQLException:未安装任何hstore扩展。       at org.postgresql.jdbc.PgPreparedStatement.setMap(PgPreparedStatement.java:553)       在org.postgresql.jdbc.PgPreparedStatement.setObject(PgPreparedStatement.java:1036)

我也尝试过:

pStmt.setString(11, dtlRec.toString());
pStmt.setObject(11, dtlRec.toString());

产生不同的错误:

  

活动JSON:{"新MbrID":29}

     

SQLException:错误:列" evtjson"是json类型,但表达式是字符变化

     

提示:您需要重写或转换表达式。

但是,至少这告诉我DB正在将列识别为JSON类型。 我确实尝试安装hstore扩展,但它告诉我它不是一个hstore对象。

OracleDocs显示了许多在preparedStatement中设置参数值的方法,但是如果有人知道答案,我宁愿不尝试所有方法。 (http://docs.oracle.com/javase/8/docs/api/java/sql/PreparedStatement.html)这些也引用了另一个参数SQLType,但我找不到对它们的任何引用。

我应该试试setAsciiStream吗? CharacterStream? CLOB?

9 个答案:

答案 0 :(得分:43)

这种行为非常烦人,因为在SQL命令中用作文字字符串时,JSON字符串被接受而没有问题。

在postgres驱动程序Github存储库中已有一个issue(即使问题似乎是服务器端处理)。

除了使用演员(参见答案) @ql_horse_with_no_name)在sql字符串中,问题作者提供了两个额外的解决方案:

  1. 在JDBC连接URL /选项中使用参数stringtype=unspecified
  2.   

    这告诉PostgreSQL实际上所有的text或varchar参数   未知类型,让它更自由地推断它们的类型。

    1. 将参数换入org.postgresql.util.PGobject
    2.  PGobject jsonObject = new PGobject();
       jsonObject.setType("json");
       jsonObject.setValue(yourJsonString);
       pstmt.setObject(11, jsonObject);
      

答案 1 :(得分:22)

你可以这样做,你只需要json字符串:

将查询更改为:

String query = "INSERT INTO table (json_field) VALUES (to_json(?::json))"

并将参数设置为String。

pStmt.setString(1, json);

答案 2 :(得分:12)

将JSON作为String传递是正确的方法,但正如错误消息所示,您需要将INSERT语句中的参数强制转换为JSON值:

insert into the_table
   (.., evtjson, ..) 
values 
   (.., cast(? as json), ..)

然后您可以使用pStmt.setString(11, dtlRec.toString())传递值

答案 3 :(得分:3)

您有两种选择:

  1. 使用statement.setString(jsonStr)然后在sql语句中处理转换:
  2. `

    
    2. Another option is to use PGobject to create a custom value wrapper.
    

    PGobject jsonObject = new PGobject(); PreparedStatement statement = con.prepareStatement("insert into table (jsonColumn) values (?)"); jsonObject.setType("json"); jsonObject.setValue(jsonStr); statement.setObject(1, jsonObject);

    {{1}}

    ` 我个人更喜欢后者,因为查询更清晰

答案 4 :(得分:2)

此处的大多数答案都定义了以非标准方式使用 jdbc 插入 postgres json 字段的方法,即。它是特定于数据库实现的。如果需要使用纯 jdbc 和纯 sql 将 java 字符串插入 postgres json 字段,请使用:

preparedStatement.setObject(1, "{}", java.sql.Types.OTHER)

这将使 postgres jdbc 驱动程序(使用 org.postgresql:postgresql:42.2.19 测试)将 java 字符串转换为 json 类型。它还会将字符串验证为有效的 json 表示,这是使用隐式字符串强制转换的各种答案所不具备的 - 导致持久化 json 数据损坏的可能性。

答案 5 :(得分:1)

而不是传递json对象传递其字符串值并将其转换为查询中的json。 示例:

JSONObject someJsonObject=..........

String yourJsonString = someJsonObject.toString();

String query = "INSERT INTO table (json_field) VALUES (to_json(yourJsonString::json))";

这对我有用。

答案 6 :(得分:0)

对于数据类型转换,PostgreSQL过于严格,非常严格。它不会隐式地将文本转换为类似文本的值,例如xml和json。

所以这个答案中的原始解决方案绕过了

的JSON验证

创建强制转换(CHARACTER VARYING as json),不带隐式函数;

此处 CHARACTER VARYING 实际上是您正在尝试传递到预准备的语句对象。

答案 7 :(得分:0)

如果使用Spring Boot:将以下行添加到application.properties帮助:

spring.datasource.hikari.data-source-properties.stringtype=unspecified

如Wero所写:

  

这告诉PostgreSQL所有的text或varchar参数实际上都是   类型未知

答案 8 :(得分:0)

正如其他人提到的那样,您的SQL字符串需要将绑定值显式转换为PostgreSQL const ts = parseInt("5fb6995",16); console.log(new Date(ts)); // 1970 console.log(new Date(ts*1000)); // still 1973 (Unix TSs are normally in seconds since Epoch - 1970/01/01) // perhaps you want hh-mm-ss from that UNIX timestamp: console.log( new Date(ts * 1000).toISOString().slice(11,-5) )json类型:

jsonb

现在您可以绑定字符串值。或者,您可以使用可以做到这一点的库,例如jOOQ(开箱即用)或Hibernate(使用第三方insert into t (id, j) values (?, ?::json) 注册)。这样做的好处是,不必每次绑定这样的变量(或读取它)时都考虑这个问题。一个jOOQ示例:

UserType

每当您使用此JSON(或JSONB)数据类型时,在幕后总是会产生与上述相同的转换。

(免责声明:我为jOOQ背后的公司工作)