Java 本机命名查询失败,出现异常“0”;列的类型为date,但表达式的类型为bytea“;当输入为NULL LocalDate时

Java 本机命名查询失败,出现异常“0”;列的类型为date,但表达式的类型为bytea“;当输入为NULL LocalDate时,java,postgresql,hibernate,spring-data,Java,Postgresql,Hibernate,Spring Data,查询: 当LocalDate值不为null时,它可以正常工作。仅当LocalDate值为null时才面临此问题 即使在PostgreSQL转换之后,它也会这样做 异常堆栈跟踪: 2018-08-30 21:10:48372--[错误]--出现了意外问题 对于您的请求org.postgresql.util.psql异常:错误:列 “成员开始日期”的类型为日期,但表达式的类型为bytea 提示:您需要重写或强制转换表达式。职位:185 在 org.postgresql.core.v3.QueryEx

查询:

当LocalDate值不为null时,它可以正常工作。仅当LocalDate值为null时才面临此问题

即使在PostgreSQL转换之后,它也会这样做

异常堆栈跟踪:

2018-08-30 21:10:48372--[错误]--出现了意外问题 对于您的请求org.postgresql.util.psql异常:错误:列 “成员开始日期”的类型为日期,但表达式的类型为bytea
提示:您需要重写或强制转换表达式。职位:185 在 org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2182) 在 org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1911) 在 org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:173) 在 org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:645) 在 org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:495) 在 org.postgresql.jdbc2.AbstractJdbc2Statement.executeQuery(AbstractJdbc2Statement.java:380)位于sun.reflect.GeneratedMethodAccessor98.invoke(未知源代码)位于 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 位于java.lang.reflect.Method.invoke(Method.java:498) org.apache.tomcat.jdbc.pool.StatementFacade$StatementProxy.invoke(StatementFacade.java:114) 位于com.sun.proxy.$Proxy185.executeQuery(未知源) org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:70) ... 省略149个公共框架

实体:

    INSERT INTO PERSON 
        (email, mobile, party_id, affiliate_id, eligibility, member_start_date, created_by, created_dt, first_name, last_name, google_connected) 
        values 
        ('xxx@yyy.org', NULL, 123, '123', '1', NULL, NULL, '2018-8-30 21:45:56.859000 -6:0:0', 'xxx', 'yyy', '0')
        ON CONFLICT (email) 
        DO UPDATE SET create_dt = '2018-8-30 21:45:56.859000 -6:0:0' where email = ?
PostgreSQL表定义;它缺少不重要的
google\u connected
列:

@Entity(name = "person")
@EqualsAndHashCode(callSuper = false)
public class PersonEntity extends Audit {

@Id
@GeneratedValue
@Column(name = "person_id", columnDefinition = "uuid", updatable = false)
private UUID id;

@Column(name = "first_name")
private String firstName;

@Column(name = "last_name")
private String lastName;

@Column(name = "email")
@NotNull
private String email;

@Column(name = "mobile")
private String mobile;

@Column(name = "party_id")
private Long partyId;

@Column(name = "affiliate_id")
private String affiliateId;

@Column(name = "eligibility")
@NotNull
private Boolean eligibility;

@Column(name = "member_start_date")
private LocalDate memberStartDate;

@Column(name = "google_connected")
private Boolean googleAccountConnected;
}

由于查询是本机的,Hibernate不知道所需的数据类型,因此当您传递null时,它默认为泛型可序列化类型处理程序。更改此行为会破坏与其他数据库的兼容性

然而,Postgres会立即解析查询并确定哪些类型是可接受的,并且它总是在检查null之前检查类型。他们是唯一能够解决这一问题的人,但拒绝这样做,并说它按预期工作

您唯一的解决方案是:

  • 使用JPQL
  • 使用托管实体
  • 在需要修改的查询字符串中使用硬编码的空值
幸运的是,对于第三个选项,使用Hibernate,您可以在本机查询中使用命名参数,这样您就不必对可用和不可用的内容进行位置计算

编辑:我发现的第四个解决方案

您有您的查询:

CREATE TABLE person
( 
   person_id             UUID NOT NULL,
   email                 VARCHAR(128) NOT NULL,
   mobile                VARCHAR(20), 
   party_id              INTEGER,
   affiliate_id          VARCHAR(20),
   eligibility              BOOLEAN NOT NULL,
   member_start_date     DATE, 
   created_by            VARCHAR(128) NOT NULL,
   created_dt            TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
   updated_by            VARCHAR(128) DEFAULT NULL,
   updated_dt            TIMESTAMP NULL,
   CONSTRAINT person_pk PRIMARY KEY ( person_id )
 );
在某个地方有一些静态的最终LocalDate对象:

Query q = em.createNativeQuery("UPDATE...");
然后按如下方式调用查询:

public static final LocalDate EPOCH_DATE = LocalDate.of(1970, 1, 1);

第一次为参数调用setParameter时,Hibernate使用该类解析类型。第二次调用它时,类型已解析,因此空值将起作用。

转到hibernate 5.1.17及更高版本+postgres似乎显示了这种行为。查看代码,当它绑定一个没有值的类型时,旧的hibernate代码试图通过typeresolver解析该类型。hibernate实现的较新版本声明它不会猜测

q.setParameter("start_date", EPOCH_DATE);
q.setParameter("start_date", nullableDateParam);

我们只是根据类型设置了一个默认值,然后是真正的空值。

您能分享您的实体类吗?这可能有助于我们了解Postgres中的表定义。您知道为什么这个问题只发生在日期列上,而不发生在其他列上吗?这取决于您在Postgres中使用的隐式类型转换函数。您可以使用
\dC
在psql控制台中列出它们,尽管我对此的理解不深。您可以在设置参数时显式地告诉hibernate类型,而不是厚颜无耻的双参数集(类型在org.hibernate.type中)。e、 g.q.setParameter(“start_date”,nullableDateParam,LocalDateType.INSTANCE)@RobertDiPaolo,它不是JPA接口的一部分。你必须进行类型转换。是的,我不确定纯JPA和Hibernate接口之间的区别是什么。然而,使用Hibernate(5.2),这对我来说是可行的(使用InstantType.INSTANT)。您的解决方案也有效,但是这样就不需要设置两次参数。这是一个旧问题,但却是一个新答案。该错误可能是由于声明的类型与为created_dt插入的值之间存在差异所致。它定义为,但插入的值是带时区的时间戳。我总是觉得价值观和定义相匹配更好;不要相信隐式翻译
  public Type resolveParameterBindType(Object bindValue) {
        if ( bindValue == null ) {
            // we can't guess
            return null;
        }