可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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.