存储json,jsonb,hstore,xml,enum,ipaddr等失败,其中"列" x"是json类型,但表达式的字符类型不同"

时间:2015-08-27 00:37:57

标签: json postgresql jpa jdbc casting

使用PostgreSQL将数据存储在类似字符串的验证类型的字段中,例如xmljsonjsonbxmlltree等,INSERTUPDATE失败,错误如下:

column "the_col" is of type json but expression is of type character varying

......或

column "the_col" is of type json but expression is of type text

为什么呢?我该怎么办呢?

我正在使用JDBC(PgJDBC)。

这是通过Hibernate,JPA和各种其他抽象层实现的。

"标准" PostgreSQL团队的建议是在SQL中使用CAST。这对于使用查询生成器或ORM的人来说没有用,特别是如果这些系统没有明确支持json这样的数据库类型,那么它们会在应用程序中通过String进行映射

某些ORM允许实现自定义类型处理程序,但我并不真的想为每个ORM编写每种数据类型的自定义处理程序,例如:关于Hibernate的json,关于EclipseLink的json,关于OpenJPA的json,关于Hibernate的xml,等等。没有用于编写通用自定义类型处理程序的JPA2 SPI。我正在寻找一般解决方案。

1 个答案:

答案 0 :(得分:31)

为什么会发生

问题在于PostgreSQL is overly strict about casts between text and non-text data types。它不允许从CAST::text)这样的文本类型进行隐式转换(SQL中没有varcharcharacter varying)类似文本的非文字类型,例如jsonxml

The PgJDBC driver specifies the data type of varchar when you call setString to assign a parameter。如果列,函数参数等的数据库类型实际上不是varchartext,而是另一种类型,则会出现类型错误。对于很多其他驱动程序和ORM也是如此。

PgJDBC:stringtype=unspecified

使用PgJDBC时的最佳选择通常为pass the parameter stringtype=unspecified。这会覆盖将setString值作为varchar传递的默认行为,而是将其留给数据库,以便猜测"他们的数据类型。在几乎所有情况下,这都完全符合您的要求,将字符串传递给您要存储的类型的输入验证器。

全部:CREATE CAST ... WITH FUNCTION ...

您可以改为CREATE CAST来定义特定于数据类型的转换,以便在逐个类型的基础上允许此操作,但这可能会在其他地方产生副作用。如果您这样做,请使用WITHOUT FUNCTION强制转换,它们会绕过类型验证并导致错误。您必须对数据类型使用输入/验证功能。使用CREATE CAST适用于其他数据库驱动程序的用户,这些驱动程序无法阻止驱动程序指定字符串/文本参数的类型。

e.g。

CREATE OR REPLACE FUNCTION json_intext(text) RETURNS json AS $$
SELECT json_in($1::cstring); 
$$ LANGUAGE SQL IMMUTABLE;

CREATE CAST (text AS json) 
WITH FUNCTION json_intext(text) AS IMPLICIT;

全部:自定义类型处理程序

如果您的ORM允许,您可以为数据类型和特定ORM实现自定义类型处理程序。当您使用可以很好地映射到PostgreSQL类型的本机Java类型而不是使用String时,这非常有用,但如果您的ORM允许您使用注释等指定类型处理程序,它也可以工作。

实现自定义类型处理程序的方法是特定于驱动程序,语言和ORM的方法。 Here's an example for Java and Hibernate for json

PgJDBC:使用PGObject

的类型处理程序

如果您在Java中使用本机Java类型,则可以扩展PGObject以为您的类型提供PgJDBC类型映射。您可能还需要实现特定于ORM的类型处理程序来使用PGObject,因为大多数ORM只会在他们无法识别的类型上调用toString。这是在Java和PostgreSQL之间映射复杂类型的首选方法,也是最复杂的。

PgJDBC:使用setObject(int, Object)

键入处理程序

如果您使用String来保存Java中的值而不是更具体的类型,则可以调用JDBC方法setObject(integer, Object)来存储不指定特定数据类型的字符串。 JDBC驱动程序将发送字符串表示,数据库将从目标列类型或函数参数类型推断出类型。

另见

问题:

外部: