hibernate/jpa-uuid的集合在使用is-null比较时无法确定数据类型

byqmnocz  于 2021-07-06  发布在  Java
关注(0)|答案(1)|浏览(483)

我将SpringBoot与PostgreSQLJDBC42.2.13和Hibernate5.3.18.final一起使用,在添加 IS NULL 检查uuid集合时,hibernate/postgresql无法确定集合的数据类型。

实体

class Element {
    @Column
    private UUID uuid;
}

存储库

@Query("SELECT e FROM Element e WHERE (:uuids) IS NULL OR e.uuid IN (:uuids)")
    List<Element> findByUuidIn(@Param("uuids") Collection<UUID> uuids);

查询执行

elementRepository.findByUuidIn(List.of(UUID.randomUUID())); // works fine!
elementRepository.findByUuidIn(null); // -> Throws exception

从跟踪日志记录绑定参数

org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [1] as [OTHER] - [null]
org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [2] as [OTHER] - [null]

例外情况

Caused by: org.postgresql.util.PSQLException: ERROR: could not determine data type of parameter $1
    at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2532)
    at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2267)
    at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:312)
    at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:448)
    at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:369)
    at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:153)
    at org.postgresql.jdbc.PgPreparedStatement.executeQuery(PgPreparedStatement.java:103)
    at jdk.internal.reflect.GeneratedMethodAccessor582.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.apache.tomcat.jdbc.pool.StatementFacade$StatementProxy.invoke(StatementFacade.java:114)
    at com.sun.proxy.$Proxy398.executeQuery(Unknown Source)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:60)
    ... 114 common frames omitted

临时解决方案

将query参数更改为string,并在query中强制转换列类型。

@Query("SELECT e FROM Element e WHERE (:uuids) IS NULL OR CAST(e.uuid AS string) IN (:uuids)")
    List<Element> findByUuidIn(@Param("uuids") Collection<String> uuids);
elementRepository.findByUuidIn(null); // works fine!
org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [1] as [VARCHAR] - [null]
org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [2] as [VARCHAR] - [null]

向社区提问

有没有办法提供 null 作为参数的uuid集合,并具有 IS NULL 检查?
是不是hibernate中没有绑定参数类型的bug UUID 和用途 OTHER 而不是使用 null 集合作为参数?
是不是postgresqljdbc中的一个bug在使用时没有确定参数的类型 OTHER 类型?

nzk0hqpo

nzk0hqpo1#

hibernate有三种基本类型Map到 java.util.UUID : org.hibernate.type.UUIDBinaryType Map到二进制数据类型( bytea ) org.hibernate.type.UUIDCharType 已Map到 CHAR ,也可以阅读
VARCHAR org.hibernate.type.PostgresUUIDType Map到postgresql uuid,通过 Types#OTHER ,符合postgresql jdbc驱动程序定义。
很多postgresql类型Map到 java.sql.Types.OTHER ,例如 org.postgresql.util.Pgobject (位变化), org.postgresql.geometric.Pgpoint (点), org.postgresql.geometric.Pgbox (方框), java.util.UUID (uuid)(看这个)。所以,当您传递时,postgresqljdbc驱动程序无法确定参数的数据类型 null .

解决方案

您可以按以下方式更正查询:

@Query("SELECT e FROM Element e WHERE (:uuids) IS NULL OR CAST(e.uuid as org.hibernate.type.UUIDCharType) IN (:uuids)")
List<Element> findByUuidIn(@Param("uuids") Collection<UUID> uuids);

它将允许您使用 Collection<UUID> 已传递参数类型。但是,实际上这与您的解决方法类似,因为它将生成以下sql:

select
   element0_.id as id1_2_ 
from TEST_SCHEMA.ELEMENT element0_ 
where  ? is null or cast(element0_.id as varchar(255)) in (?)

您可以更改表的列定义:

create table ELEMENT
(
   id bytea,
   ...
);

然后按以下方式Map此列:

import org.hibernate.annotations.Type;

@Entity
public class Element
{
   @Id
   @GeneratedValue
   @Type(type = "uuid-binary") // This is pg-uuid by default for PostgreSQL82Dialect and higher
   private UUID id;

   // ...
}

然后您就可以查询它而无需任何强制转换:

Collection<UUID> uuids = null;

List<Element> elements = em.createQuery(
   "select e from Element e where (:uuids) is null or e.id in (:uuids)",
   Element.class)
.setParameter("uuids", uuids)
.getResultList();

相关问题