В PostgreSQL у меня есть таблица
CREATE TABLE public.my_table
(
id integer NOT NULL,
...
Я хочу выполнить запрос: Покажи мне строки с заданным идентификатором. Если id равен нулю, покажи мне все строки.
Я пробовал это с
public interface MyRepository extends JpaRepository<MyTable, Integer> {
@Query(value = "SELECT * FROM my_table WHERE (?1 IS NULL OR id = ?1)", nativeQuery = true)
List<MyTable> findAll(Integer id);
Если id != null, все нормально. Но если id == null, я получу ошибку
org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:261) ~[spring-orm-4.3.13.RELEASE.jar:4.3.13.RELEASE]
...
Caused by: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:106) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
...
Caused by: org.postgresql.util.PSQLException: ERROR: operator does not exist: integer = bytea
Hint: No operator matches the given name and argument type(s). You might need to add explicit type casts.
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2440) ~[postgresql-42.2.5.jar:42.2.5]
...
Очевидно, что оценка короткого замыкания не работает, и null преобразуется в bytea.
В качестве обходного пути я изменил значение запроса на
SELECT * FROM my_table WHERE (?1 IS NULL OR id = (CAST (CAST(?1 AS character varying) AS integer)))
Но это неприятно, потому что int снова преобразуется в строку и в int. У вас есть лучшее решение, например лучший приведение или запрос sql?





Другой обходной путь для этого - создать запрос вручную из EntityManager (em в примере) и вызвать для него setParameter один раз с ненулевым значением, а затем снова с реальным значением.
private static final Integer exampleInt = 1;
List<MyTable> findAll(Integer id) {
return em.createNativeQuery("SELECT * FROM my_table WHERE (:id IS NULL OR id = :id)", MyTable.class)
.setParameter("id", exampleInt)
.setParameter("id", id)
.resultList();
}
Это гарантирует, что Hibernate знает тип значения при следующем втором вызове, даже если оно равно null.
Ошибка в сервере PostgreSQL, а не в Hibernate, но они отказались исправить это, потому что он работает, как задумано. У вас есть всего несколько сотен типов SQL NULL на сервере, и они по большей части несовместимы друг с другом, хотя предполагается, что это одно особое особое значение.
Просто оставив это здесь, PostgreSQL утверждает, что они правильно реализовали спецификацию JDBC и что способ передачи
nullв JDBC Hibernate неверен, потому что они отправляют «типизированный ноль», а не константу нуль. Если это действительно то, что говорит спецификация JDBC (которую я не проверял), тогда ошибка будет в Hibernate, даже если трюк, который они делают, правильно работает для других баз данных.