PostgreSQL JDBC Null String taken as a bytea

匿名 (未验证) 提交于 2019-12-03 02:00:02

问题:

If entity.getHistory() is null following code snippet:

(getEntityManager() returns spring injected EntityManager, database field history type is: text or varchar2(2000)

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

Gives strange exception:

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** 

Hint: You will need to rewrite or cast the expression.

Problem occurred only in this configuration:
OS: CentOS release 5.6 (Final)
Java: 1.6.0_26
DB: PostgreSQL 8.1
JDBC Driver: postgresql-9.1-901.jdbc4
Application Server: apache-tomcat-6.0.28

Everything is working fine on few other configurations or when history is empty string. Same statement executed from pgAdmin works fine.

I guess problem is in PostgreSQL JDBC driver, is there some sensible reason to treat null string as a bytea value? Maybe some strange changes between Postgres 8 and 9?

回答1:

Since you're calling setParameter(int,Object) with a null value, at a guess the entity manager has no idea which persistence type to use to bind the parameter. Istr Hibernate having a predilection for using SerializableType, which would equate to a bytea.

Can you use the setParameter method that takes a Parameter object instead, so you can specify the type? I haven't really used JPA, so can't give a detailed pointer.

(IMHO this is your just desserts for abusing EntityManager's native-sql query interface to do inserts, rather than actually mapping the entity, or just dropping through to JDBC)



回答2:

There's a simple fix: when you're building the query string, if a parameter is going to be null -- use "null" instead of "?".

As @araqnid says, Hibernate is incorrectly casting null values into type bytea because it doesn't know any better.



回答3:

If you are willing to use the PreparedStatement class instead of Query:

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

(It is possible that using ?::text in your query string would also work, but I've never done it that way myself.)



回答4:

Using Hibernate specific Session API works as a workaround:

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(); 

I've also filed HHH-9165 to report this issue, if it is indeed a bug.



回答5:

The JDBC setNull with sql type parameter is fallback for legacy JDBC drivers that do not pass the JDBC Driver Test Suite.

One can guard against null errors within the query as follows:

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

This should work in Postgresql after the fix of this issue.



易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!