Хранение json, jsonb, hstore, xml, enum, ipaddr и т.д. Терпит неудачу с "столбец" x "имеет тип json, но выражение имеет характерный характер,

При использовании PostgreSQL для хранения данных в поле типа типа, например, xml, json, jsonb, xml, ltree и т.д., INSERT или UPDATE с ошибкой:

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 является использование CAST в SQL. Это не полезно для людей, использующих генераторы запросов или ORM, особенно если эти системы не имеют явной поддержки типов баз данных, таких как json, поэтому они отображаются в приложении String.

Некоторые ORM разрешают реализацию обработчиков настраиваемого типа, но я действительно не хочу писать пользовательский обработчик для каждого типа данных для каждого ORM, например. json on Hibernate, json on EclipseLink, json on OpenJPA, xml на Hibernate и т.д. Там нет JPA2 SPI для написания универсального обработчика настраиваемого типа. Я ищу общее решение.

Ответы

Ответ 1

Почему это происходит

Проблема заключается в том, что PostgreSQL слишком строг в отношении переходов между текстовыми и нетекстовыми типами данных. Он не позволит неявное преобразование (одно без CAST или :: в SQL) из текстового типа, например text или varchar (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;

Все: обработчик пользовательского типа

Если ваш ORM разрешает, вы можете реализовать обработчик настраиваемого типа для типа данных и этого конкретного ORM. Это в основном полезно, когда вы используете собственный тип Java, который хорошо отображает тип PostgreSQL, вместо использования 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 отправит строковое представление, и база данных выведет тип из типа столбца назначения или типа аргумента функции.

См. также

Вопросы:

Внешний: