【问题标题】:PostgreSQL JDBC Null String taken as a byteaPostgreSQL JDBC Null String 被当作一个字节
【发布时间】:2012-01-02 21:58:22
【问题描述】:

如果 entity.getHistory() 在代码 sn-p 之后为空:

(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 版(最终版)
Java:1.6.0_26
数据库:PostgreSQL 8.1
JDBC 驱动程序:postgresql-9.1-901.jdbc4
应用服务器:apache-tomcat-6.0.28

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

我猜问题出在 PostgreSQL JDBC 驱动程序中,是否有合理的理由将空字符串视为字节值?也许 Postgres 8 和 9 之间有一些奇怪的变化?

【问题讨论】:

  • 我怀疑 EntityManager 为 NULL 值调用了错误的 PreparedStatement.setXXX() 方法。您也可以尝试 8.1 JDBC 驱动程序。顺便说一句:您是否知道不再支持 8.1?
  • 你能澄清一下你的前两段吗?它们似乎没有意义。
  • 相同的代码在 PostgreSQL 9+(@a_horse_with_no_name 客户端必须有 8.1...)、Windows 7 等环境下运行良好。如果 entity.getHistory() 为 "" 而不是 null,则它可以正常工作。从 pgAdmin 执行的相同插入工作正常。
  • @laidlook:您是否针对 8.1 数据库尝试了 8.1 驱动程序? (但我仍然认为 EntityManager 的事情有问题)
  • 有点。我无权访问该服务器,我所能做的就是要求客户做这种事情。他们没有结果,所以他们说...悲伤的开发人员生活 :) 我将尝试在 VM 上创建此配置。

标签: string postgresql hibernate jdbc null


【解决方案1】:

有一个简单的解决方法:在构建查询字符串时,如果参数为空 - 使用“null”而不是“?”。

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

【讨论】:

    【解决方案2】:

    由于您使用空值调用setParameter(int,Object),因此猜测实体管理器不知道使用哪种持久性类型来绑定参数。 Istr Hibernate 偏爱使用 SerializableType,这相当于一个字节。

    你可以使用setParameter方法来代替参数对象,这样你就可以指定类型吗?没有真正用过JPA,所以不能给出详细的指点。

    (恕我直言,这是您滥用 EntityManager 的 native-sql 查询接口进行插入,而不是实际映射实体,或者只是直接通过 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 来报告这个问题。

      【讨论】:

      • 注意我遇到了Short 的问题,你不能使用setShort(),因为它需要一个不可为空的short。使用休眠 4.x 的解决方案是:.setParameter("short", ShortValue, ShortType.INSTANCE);(使用 3.x 我认为是Hibernate.SHORT 而不是使用ShortType)。
      • 试图让 JPA 进入休眠状态数小时。谢谢!
      • update 语句的新问题:hibernate.atlassian.net/browse/HHH-14778
      【解决方案4】:

      以上大部分答案都很有意义,帮助我缩小问题范围。

      我解决了这样的空值问题:

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

      【讨论】:

      • 对我来说 .setParameter("yearLow",yearLow, IntegerType.INSTANCE) 成功了。
      【解决方案5】:

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

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

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

      【讨论】:

        【解决方案6】:

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

        之前:

        @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)
        

        【讨论】:

          【解决方案7】:

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

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

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

          在修复 issue 之后,这应该可以在 Postgresql 中使用。

          【讨论】:

            【解决方案8】:

            在我的 springboot 应用程序中遇到了这个问题,使用 postgres 和 hibernate。 我需要提供来自/到参数的日期,每个组合都可能为空。

            查询摘录:

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

            道呼:

            @Transactional(readOnly = true)
            public List<PriceListDto> ReportCatPriceList(Long idk, Date dateFrom, Date dateTo) {
                Query query = EM.createNativeQuery(queryReport, PersonProductsByDay.class)
                    .setParameter("idCat", idk)
                    .setParameter("datefrom", dateFrom, TemporalType.DATE)
                    .setParameter("dateto", dateTo, TemporalType.DATE);
            
                return query.getResultList();
            }
            

            【讨论】:

              【解决方案9】:

              我在使用 Oracle 数据库执行 UPDATE 时遇到了同样的问题。 有时,一些更新的字段为空,就会出现此问题。显然 JPA 正在将我的 null 值转换为 bytea,正如本主题中已经谈到的那样。

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

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

              【讨论】:

                猜你喜欢
                • 2014-03-02
                • 2012-01-12
                • 1970-01-01
                • 2015-08-08
                • 2017-05-13
                • 1970-01-01
                • 2020-12-04
                • 2023-03-24
                • 2019-03-29
                相关资源
                最近更新 更多