PostgreSQL JDBC Null String, взятый как bytea

Если объект entity.getHistory() равен null, следуя фрагменту кода:

(getEntityManager() возвращает spring введенный EntityManager, тип истории поля базы данных: text или varchar2 (2000)

Query query = getEntityManager().createNativeQuery("insert into table_name(..., history, ....) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
[...]
.setParameter(6, entity.getHistory())
[...]

query.executeUpdate();

Дает странное исключение:

17/11/11 06:26:09:009 [pool-2-thread-1]  WARN util.JDBCExceptionReporter:100 - SQL Error: 0, SQLState: 42804
17/11/11 06:26:09:009 [pool-2-thread-1] ERROR util.JDBCExceptionReporter:101 - ERROR: **column "history" is of type text but expression is of type bytea**

Подсказка: вам нужно будет переписать или применить выражение.

Проблема возникает только в этой конфигурации:
ОС: релиз CentOS 5.6 (Final)
Java: 1.6.0_26
DB: PostgreSQL 8.1
Драйвер JDBC: postgresql-9.1-901.jdbc4
Сервер приложений: apache-tomcat-6.0.28

Все работает отлично на нескольких других конфигурациях или когда история пуста. Тот же оператор, выполняемый из pgAdmin, отлично работает.

Я думаю, проблема в драйвере PostgreSQL JDBC, есть ли разумная причина рассматривать пустую строку как значение bytea? Может быть, некоторые странные изменения между Postgres 8 и 9?

Ответ 1

Там простое исправление: когда вы строите строку запроса, если параметр имеет значение null, используйте "null" вместо "?".

Как говорит @araqnid, Hibernate неправильно вводит нулевые значения в тип bytea, потому что он не знает ничего лучшего.

Ответ 2

Поскольку вы вызываете setParameter(int,Object) с нулевым значением, угадайте, что диспетчер объектов не знает, какой тип персистентности использовать для привязки параметра. Istr Hibernate имеет склонность к использованию SerializableType, который будет равен байту.

Можно ли использовать метод setParameter, который принимает объект Parameter, поэтому вы можете указать тип? Я не использовал JPA, поэтому не могу дать подробный указатель.

(IMHO это ваши десерты для злоупотребления интерфейсом запросов native-sql EntityManager для вставки, а не для фактического отображения объекта или просто перехода на JDBC)

Ответ 3

Использование Hibernate специфического Session API работает как обходной путь:

String sql = "INSERT INTO person (id, name) VALUES (:id, :name)";
Session session = em.unwrap(Session.class);
SQLQuery insert = session.createSQLQuery(sql);
sql.setInteger("id", 123);
sql.setString("name", null);
insert.executeUpdate();

Я также написал HHH-9165, чтобы сообщить об этой проблеме, если это действительно ошибка.

Ответ 4

Если вы хотите использовать класс PreparedStatement вместо запроса:

if (entity.getHistory() == null)
    stmt.setNull(6, Types.VARCHAR);
else
    stmt.setString(6, entity.getHistory());

(Возможно, использование строки ?::text в вашей строке запроса также будет работать, но я никогда не делал этого так.)

Ответ 5

JDBC setNull с параметром sql type является резервным для устаревших драйверов JDBC, которые не передают тестовый набор драйверов JDBC.

Можно защитить от нулевых ошибок в запросе следующим образом:

SELECT a FROM Author a WHERE: lastName IS NULL ИЛИ LOWER (a.lastName) =: lastName

Это должно работать в Postgresql после исправления этого issue.

Ответ 6

Большинство из приведенных выше ответов имеют смысл и помогли мне сузить вопрос.

Я исправил проблему для нулей следующим образом:

setParameter(8, getDate(), TemporalType.DATE);