简体   繁体   English

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

[英]How can I Insert JSON object into Postgres using Java preparedStatement?

I'm struggling to insert a JSON object into my postgres v9.4 DB.我正在努力将 JSON 对象插入到我的 postgres v9.4 数据库中。 I have defined the column called "evtjson" as type json (not jsonb ).我已将名为“evtjson”的列定义为json类型(不是jsonb )。

I am trying to use a prepared statement in Java (jdk1.8) to insert a Json object (built using JEE javax.json libraries) into the column, but I keep running into SQLException errors.我正在尝试使用 Java (jdk1.8) 中的准备语句将 Json 对象(使用 JEE javax.json 库构建)插入到列中,但我一直遇到 SQLException 错误。

I create the JSON object using:我使用以下方法创建 JSON 对象:

JsonObject mbrLogRec = Json.createObjectBuilder().build();
…
mbrLogRec = Json.createObjectBuilder()
                .add("New MbrID", newId)
                .build();

Then I pass this object as a parameter to another method to write it to the DB using a prepared statement.然后我将此对象作为参数传递给另一个方法,以使用准备好的语句将其写入数据库。 (along with several other fields) As: (以及其他几个字段)如:

pStmt.setObject(11, dtlRec);

Using this method, I receive the following error:使用这种方法,我收到以下错误:

org.postgresql.util.PSQLException: No hstore extension installed. org.postgresql.util.PSQLException: 没有安装 hstore 扩展。 at org.postgresql.jdbc.PgPreparedStatement.setMap(PgPreparedStatement.java:553) at org.postgresql.jdbc.PgPreparedStatement.setObject(PgPreparedStatement.java:1036)在 org.postgresql.jdbc.PgPreparedStatement.setMap(PgPreparedStatement.java:553) 在 org.postgresql.jdbc.PgPreparedStatement.setObject(PgPreparedStatement.java:1036)

I have also tried:我也试过:

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

Which produce a different error:产生不同的错误:

Event JSON: {"New MbrID":29}事件 JSON: {"New MbrID":29}

SQLException: ERROR: column "evtjson" is of type json but expression is of type character varying SQLException:错误:列“evtjson”的类型为 json,但表达式的类型为不同的字符

Hint: You will need to rewrite or cast the expression.提示:您需要重写或转换表达式。

But, at least this tells me that the DB is recognizing the column as type JSON.但是,至少这告诉我数据库将列识别为 JSON 类型。 I did try installing the hstore extension, but it then told me that it was not an hstore object.我确实尝试安装 hstore 扩展,但它告诉我它不是 hstore 对象。

OracleDocs shows a number of various methods to set the parameter value in the preparedStatement, but I'd rather not try them all if someone knows the answer. OracleDocs 显示了许多不同的方法来设置preparedStatement 中的参数值,但如果有人知道答案,我宁愿不尝试所有这些方法。 ( http://docs.oracle.com/javase/8/docs/api/java/sql/PreparedStatement.html ) These also reference an additional parameter, SQLType, but I can't find any reference to these. ( http://docs.oracle.com/javase/8/docs/api/java/sql/PreparedStatement.html ) 这些还引用了一个附加参数 SQLType,但我找不到对这些的任何引用。

Should I try setAsciiStream ?我应该尝试setAsciiStream吗? CharacterStream ? CharacterStream CLOB?克洛布?

This behaviour is quite annoying since JSON strings are accepted without problems when used as literal strings in SQL commands.这种行为很烦人,因为当在 SQL 命令中用作文字字符串时,JSON 字符串被接受没有问题。

There is a already an issue for this in the postgres driver Github repository (even if the problem seems the be the serverside processing). postgres 驱动程序 Github 存储库中已经存在一个问题(即使问题似乎是服务器端处理)。

Besides using a cast (see answer of @a_horse_with_no_name) in the sql string, the issue author offers two additional solutions:除了在 sql 字符串中使用强制转换(参见@a_horse_with_no_name 的答案)之外,问题作者还提供了两个额外的解决方案:

  1. Use a parameter stringtype=unspecified in the JDBC connection URL/options.在 JDBC 连接 URL/选项中使用参数stringtype=unspecified

This tells PostgreSQL that all text or varchar parameters are actually of unknown type, letting it infer their types more freely.这告诉 PostgreSQL 所有 text 或 varchar 参数实际上都是未知类型,让它更自由地推断它们的类型。

  1. Wrap the parameter in a org.postgresql.util.PGobject :将参数包装在org.postgresql.util.PGobject

 PGobject jsonObject = new PGobject();
 jsonObject.setType("json");
 jsonObject.setValue(yourJsonString);
 pstmt.setObject(11, jsonObject);

You can do it like this and you just need the json string:你可以这样做,你只需要 json 字符串:

Change the query to:将查询更改为:

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

And set the parameter as a String.并将参数设置为字符串。

pStmt.setString(1, json);

Passing the JSON as a String is the right approach, but as the error message tells you, you need to cast the parameter in the INSERT statement to a JSON value:将 JSON 作为字符串传递是正确的方法,但正如错误消息告诉您的那样,您需要将INSERT语句中的参数转换为JSON值:

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

Then you can use pStmt.setString(11, dtlRec.toString()) to pass the value然后你可以使用pStmt.setString(11, dtlRec.toString())来传递值

You have two options:您有两个选择:

  1. Use statement.setString(jsonStr) and then handle the conversion in the sql statement:使用 statement.setString(jsonStr) 然后处理 sql 语句中的转换:

` `

PreparedStatement statement = con.prepareStatement("insert into table 
 (jsonColumn) values (?::json)");
 statement.setString(1, jsonStr);

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);

` I personally prefer the latter as the query is cleaner ` 我个人更喜欢后者,因为查询更清晰

As others have mentioned, your SQL string needs to explicitly cast the bind value to the PostgreSQL json or jsonb type:正如其他人提到的,您的 SQL 字符串需要将绑定值显式转换为 PostgreSQL jsonjsonb类型:

insert into t (id, j) values (?, ?::json)

Now you can bind the string value.现在您可以绑定字符串值。 Alternatively, you can use a library that can do it, for example jOOQ (works out of the box) or Hibernate (using a third party UserType registration).或者,您可以使用可以执行此操作的库,例如jOOQ (开箱即用)或Hibernate (使用第三方UserType注册)。 The benefits of this is that you don't have to think about this every time you bind such a variable (or read it).这样做的好处是每次绑定这样的变量(或读取它)时都不必考虑这一点。 A jOOQ example:一个 jOOQ 示例:

ctx.insertInto(T)
   .columns(T.ID, T.J)
   .values(1, JSON.valueOf("[1, 2, 3]"))
   .execute();

Behind the scenes, the same cast as above is always generated, whenever you work with thisJSON (or JSONB ) data type.在幕后,每当您使用此JSON (或JSONB )数据类型时,总是会生成与上述相同的演员表。

(Disclaimer: I work for the company behind jOOQ) (免责声明:我为 jOOQ 背后的公司工作)

Most answers here defines ways of inserting into postgres json field with jdbc in a non-standard way, ie.这里的大多数答案都定义了以非标准方式使用 jdbc 插入 postgres json 字段的方法,即。 it is db implementation specific.它是特定于数据库实现的。 If you need to insert a java string into a postgres json field with pure jdbc and pure sql use:如果需要使用纯 jdbc 和纯 sql 将 java 字符串插入 postgres json 字段,请使用:

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

This will make the postgres jdbc driver (tested with org.postgresql:postgresql:42.2.19) convert the java string to the json type.这将使 postgres jdbc 驱动程序(使用 org.postgresql:postgresql:42.2.19 测试)将 java 字符串转换为 json 类型。 It will also validate the string as being a valid json representation, something that various answers using implicit string casts does not do - resulting in the possibility of corrupt persisted json data.它还会将字符串验证为有效的 json 表示,这是使用隐式字符串强制转换的各种答案所不具备的 - 导致持久化 json 数据损坏的可能性。

PostgreSQL is excessively, annoyingly strict about data type conversions. PostgreSQL过于严格,对数据类型转换非常严格。 It won't implicitly cast text even to text-like values such as xml and json. 即使是像xml和json这样的类似文本的值,它也不会隐式转换文本。

so the original solution in this answer bypasses JSON validation by 因此此答案中的原始解决方案通过以下方法绕过JSON验证:

create cast (CHARACTER VARYING as json) without function as implicit; 创建强制类型转换(以CHARACTER VARYING为json),而不使用隐式函数;

here CHARACTER VARYING is actually you are trying to pass to your prepared statement object. 这里的CHARACTER VARYING实际上是您试图传递给准备好的语句对象的地方。

if using spring boot: adding the following line to application.properties helped:如果使用 spring boot:将以下行添加到 application.properties 有帮助:

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

as Wero wrote:正如韦罗所写:

This tells PostgreSQL that all text or varchar parameters are actually of unknown type这告诉 PostgreSQL 所有 text 或 varchar 参数实际上都是未知类型

Instead of passing json object pass its string value and cast it to json in the query.不是传递 json 对象,而是传递其字符串值并将其转换为查询中的 json。 Example:例子:

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

String yourJsonString = someJsonObject.toString();

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

this worked for me.这对我有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM