【问题标题】:Storing json, jsonb, hstore, xml, enum, ipaddr, etc fails with "column "x" is of type json but expression is of type character varying"存储 json、jsonb、hstore、xml、enum、ipaddr 等失败,“列“x”是 json 类型,但表达式的类型是字符变化”
【发布时间】:2023-03-30 00:23:01
【问题描述】:

当使用 PostgreSQL 将数据存储在类似字符串的验证类型的字段中时,例如 xmljsonjsonbxmlltree 等,INSERT 或 @987654327 @ 失败并出现如下错误:

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。我正在寻找一个通用的解决方案。

【问题讨论】:

    标签: json postgresql jpa jdbc casting


    【解决方案1】:

    为什么会发生

    问题在于PostgreSQL is overly strict about casts between text and non-text data types。它不允许从 textvarchar (character varying) 等文本类型到类似文本的非文本类型(如 @ 987654337@、xml

    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 适用于其他数据库驱动程序的用户,这些驱动程序无法停止驱动程序指定字符串/文本参数的类型。

    例如

    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 类型而不是使用 String 的本机 Java 类型时,这非常有用,尽管如果您的 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 驱动程序将发送字符串表示,数据库将根据目标列类型或函数参数类型推断类型。

    另见

    问题:

    外部:

    【讨论】:

    • 非常感谢。 FWIW,在测试它时 stringtype=unspecified 为我工作,另一方面 setObject(int, Object) 没有(至少没有激活 stringtype=unspecified)。
    • 如何用弹簧靴做到这一点?
    • @roberttrudel 请发布一个新问题
    • 很好的答案,但我想补充一点:在某些情况下,仅使用 setObject(int, Object) 对 PgJDBC 来说是不够的 - 它仍然足够聪明地确定传递的值实际上是一个字符串,因此提供 oid = 1043 作为参数类型(对我来说,这会导致 Postgre 端出现错误,例如 ERROR: column type is 'jsonb' and passed value is of type 'character varying'。我必须使用 setObject(int, Object, java.sql.Types.OTHER) 来达到必要的效果(参数 oid = 0,它基本上告诉服务器“猜猜你自己是哪种类型它是")
    猜你喜欢
    • 2016-05-15
    • 2017-03-28
    • 2021-04-05
    • 2020-12-28
    • 2017-07-22
    • 2015-05-30
    • 2016-10-19
    • 2019-11-08
    • 1970-01-01
    相关资源
    最近更新 更多