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

Cra*_*ger 20 postgresql json casting jpa jdbc

当使用PostgreSQL将数据存储在一个绳状验证类型的字段,如xml,json,jsonb,xml,ltree,等等,所述INSERTUPDATE失败等错误:

column "the_col" is of type json but expression is of type character varying
Run Code Online (Sandbox Code Playgroud)

... 要么

column "the_col" is of type json but expression is of type text
Run Code Online (Sandbox Code Playgroud)

为什么?我能做些什么呢?

我正在使用JDBC(PgJDBC).

这通过Hibernate,JPA和各种其他抽象层发生.

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

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

Cra*_*ger 31

为什么会这样

问题是PostgreSQL对文本和非文本数据类型之间的强制转换过于严格.它不会允许隐式转换(一个没有CAST::从文本类型像SQL)textvarchar(character varying)的文本样非文本类型等json,xml等.

PgJDBC驱动程序指定varchar调用setString分配参数时的数据类型.如果列,函数参数等的数据库类型实际上不是,varchar或者是text另一种类型,则会出现类型错误.对于很多其他驱动程序和ORM也是如此.

PgJDBC: stringtype=unspecified

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

所有: CREATE CAST ... WITH FUNCTION ...

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

例如

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;
Run Code Online (Sandbox Code Playgroud)

全部:定制类型处理程序

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

实现自定义类型处理程序的方法是特定于驱动程序,语言和ORM的方法.这是Java和Hibernate的一个例子json.

PgJDBC:使用类型处理程序 PGObject

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

PgJDBC:使用类型处理程序 setObject(int, Object)

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

也可以看看

问题:

外部:

  • 非常感激。FWIW,在测试它时,`stringtype=unspecified` 对我有用,另一方面,`setObject(int, Object)` 没有(至少在没有激活 `stringtype=unspecified` 的情况下)。 (2认同)