PostgreSQL JDBC Null字符串作为bytea

时间:2011-11-21 11:38:00

标签: string postgresql hibernate jdbc null

如果在代码片段之后entity.getHistory()为null:

(getEntityManager()返回弹簧注入的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版(最终版)
Java:1.6.0_26
DB:PostgreSQL 8.1
JDBC驱动程序:postgresql-9.1-901.jdbc4
Application Server:apache-tomcat-6.0.28

在其他一些配置或历史记录为空字符串时,一切正常。 从pgAdmin执行的相同语句工作正常。

我猜问题是在PostgreSQL JDBC驱动程序中,是否有一些明智的理由将空字符串视为bytea值? Postgres 8和9之间可能有一些奇怪的变化吗?

9 个答案:

答案 0 :(得分:5)

有一个简单的修复:当你构建查询字符串时,如果参数为null - 使用“null”而不是“?”。

正如@araqnid所说,Hibernate错误地将null值转换为bytea类型,因为它不知道更好。

答案 1 :(得分:4)

由于您使用空值调用setParameter(int,Object),因此猜测实体管理器不知道用于绑定参数的持久性类型。 Istr Hibernate倾向于使用SerializableType,它等同于bytea。

您可以使用带有Parameter对象的setParameter方法,这样您可以指定类型吗?我还没有真正使用过JPA,所以无法给出详细的指针。

(恕我直言,这是你滥用EntityManager的native-sql查询接口进行插入,而不是实际映射实体,或只是直接进入JDBC)的唯一甜点。

答案 2 :(得分:2)

使用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报告此问题,如果它确实是个错误。

答案 3 :(得分:1)

如果您愿意使用PreparedStatement类而不是Query:

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

(在您的查询字符串中使用?::text可能也有效,但我自己从未这样做过。)

答案 4 :(得分:1)

您可以在查询中将参数转换为适当的类型。我认为该解决方案也应该适用于其他数据库。

之前:

@Query("SELECT person FROM people person"
     + " WHERE (?1 IS NULL OR person.name = ?1)")
List<Person> getPeopleWithName(final String name);

之后:

@Query("SELECT person FROM people person"
     + " WHERE (?1 IS NULL OR person.name = cast(?1 as string))")
List<Person> getPeopleWithName(final String name);

也可用于LIKE语句:

person.name LIKE concat('%', cast(?1 as string), '%')

并带有数字(此处为Double类型):

person.height >= cast(cast(?1 as string) as double)

答案 5 :(得分:0)

带有sql type参数的JDBC setNull是未通过JDBC驱动程序测试套件的旧JDBC驱动程序的后备。

可以防止查询中出现空错误,如下所示:

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

在修复此issue后,这应该适用于Postgresql。

答案 6 :(得分:0)

以上大多数答案都很有意义,有助于我缩小问题范围。

我修复了以下null的问题:

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

答案 7 :(得分:0)

在我的springboot应用程序中使用postgres和hibernate出现了此问题。 我需要提供从/到参数的日期,每种组合都可能包含空值。

查询摘录:

drop table if exists t;
create table t
(id int,status  varchar(20),  data varchar(3),date varchar(10));
insert into t values
(  1 , 'START'   ,  'a4c'   , 'Jan 1'),
(  2 , 'WORKING' ,  '2w3'   , 'Dec 29'),
(  3 , 'WORKING' ,  '2d3'   , 'Dec 29'),
(  4 , 'WORKING' ,  '3ew'   , 'Dec 26'),
(  5 , 'WORKING' ,  '5r5'   , 'Dec 23'),
(  6 , 'START'   ,  '2q3'   , 'Dec 22'),
(  7 , 'WORKING' ,  '32w'   , 'Dec 20'),
(  8 , 'WORKING' ,  '9k5'   , 'Dec 10');

SELECT  MIN(ID) ID,
            'START' STATUS,
            SUM(CASE WHEN STATUS <> 'START' THEN 1 ELSE 0 END) AS OBS,
            Max(DATE) DATE
FROM
(
select t.*,
        CASE WHEN STATUS = 'START' THEN DATE ELSE '' END AS DT,
        COALESCE(
        (select t1.id from t t1 where t1.STATUS = 'START' and t1.id > t.id ORDER BY T1.ID limit 1) 
        ,99999) NEXTID
from t
) S 
GROUP BY NEXTID;    
+------+--------+------+--------+
| ID   | STATUS | OBS  | DATE   |
+------+--------+------+--------+
|    1 | START  |    4 | Jan 1  |
|    6 | START  |    2 | Dec 22 |
+------+--------+------+--------+
2 rows in set (0.00 sec)

道电话:

   ...
        WHERE  f.idf = :idCat
        AND f.supplier = TRUE
        AND COALESCE (pc.datefrom, CAST('2000-1-1' AS DATE) ) <= COALESCE (:datefrom, now())
        AND COALESCE (pc.dateto, CAST('2200-12-31' AS DATE) ) >= COALESCE (:dateto, now())

答案 8 :(得分:0)

我在使用Oracle数据库执行UPDATE时遇到相同的问题。 有时,一些要更新的字段为空,并且会发生此问题。显然,如本主题中所述,JPA正在将我的空值投射到bytea中。

我放弃使用Query对象更新表并使用EntityManager.merge(Entity)方法。

我只创建带有所有信息的Entity对象,当我执行merge(Entity)时,JPA执行SELECT,将已经存在数据库中的信息与我的Entity对象进行比较,并正确执行UPDATE。也就是说,JPA为空的Entity变量创建一个“ field = NULL”语句。