Simple Native SQL with NULL Parameter fails on Oracle
Description
relates to
Activity
Andreas Loew March 2, 2022 at 10:41 AM
This is already existing as a Spring Data JPA issue, but it’s still open and not assigned since three months - please join mie in voting for the Spring Data JPA issue… :-)
https://github.com/spring-projects/spring-data-jpa/issues/2370
Christian Beikov March 2, 2022 at 10:33 AM
Spring Data could pass TypedParameterValue
values to avoid this issue, yes. But that is something you should request in the Spring community.
Andreas Loew March 2, 2022 at 10:23 AM
Is there also any chance to bring together the TypedParameterValue
approach with declarative Queries like in Spring Data JPA repositories?
Such as, when I have within a Spring JpaRepository<TfzAbrechnungsportion, Long>
@Query(value = "SELECT (...)"
+ "FROM (...) "
+ "WHERE (...) AND ap.ende <= COALESCE(:zeitpunktEnde, ap.ende)"
List<TfzAbrechnungsportion> getStatusHistory(@Param("vens") String vens,
@Param("zeitpunktBeginn") Date zeitpunktBeginn, @Param("zeitpunktEnde") Date zeitpunktEnde);
being able to make the JDBC driver not bind the nullable param "zeitpunktEnde
" to the type “VARBINARY
" , but use a TypedParameterValue
approach?
Currently, I don't see any other way than to explicitly fully implement the JpaRepository method in “old style” using EntityManager API and use TypedParameterValue
to set parameter values…!?
Thanks a million!
Christian Beikov March 2, 2022 at 8:51 AM
In case the type is unknown like in your example, Hibernate 6.0 now tries to ask the JDBC driver about the parameter type and has smarter fallbacks. I’m pretty sure that 6.0.0.CR1 fixes this already, but in general, all of this is dependent on the support of the JDBC driver. To make this work for all drivers, you will have to set a TypedParameterValue
instead.
Encounter the similar problem in 5.3.7 which is described at:
https://hibernate.atlassian.net/browse/HHH-13155
Entity:
@Entity @Table(name = "ADDRESS") @Data public class Address { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "adddr_seq") @SequenceGenerator(name = "adddr_seq", allocationSize = 1) private Long id; @Column(name = "ADDRESS_VALUE") private String addressValue; @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "PERSON_ID") private Person person; }
Native query with null parameter for NUMBER column type:
Query nativeQuery = em.createNativeQuery("select * from ADDRESS a where (? is null OR a.ID = ?)", Address.class); nativeQuery.setParameter(1, null); nativeQuery.setParameter(2, null); List resultList = nativeQuery.getResultList();
Exception thrown for version 5.4.27.Final:
SQL Error: 932, SQLState: 42000 Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not extract ResultSet] with root cause oracle.jdbc.OracleDatabaseException: ORA-00932: inconsistent datatypes: expected NUMBER got BINARY
When database column is VARCHAR2, exception didn't occur:
Query nativeQuery = em.createNativeQuery("select * from ADDRESS a where (? is null OR a.address_value = ?)", Address.class); nativeQuery.setParameter(1, null); nativeQuery.setParameter(2, null); List resultList = nativeQuery.getResultList(); System.out.println(resultList);
I also tried to execute same code with EclipseLink JPA provider and everything was fine.
I did test with Hibernate version org.hibernate.orm:hibernate-core:6.0.0.Alpha6 :
2021-01-17 20:36:04.343 TRACE 3184 --- [nio-8080-exec-1] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [JAVA_OBJECT] - [null] 2021-01-17 20:36:05.439 WARN 3184 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 17004, SQLState: 99999 2021-01-17 20:36:05.439 ERROR 3184 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : Invalid column type: 2000 2021-01-17 20:36:05.462 ERROR 3184 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: JDBC exception executing SQL [select * from ADDRESS a where (? is null OR a.id = ?)]] with root cause java.sql.SQLException: Invalid column type: 2000 at oracle.jdbc.driver.OracleStatement.getInternalType(OracleStatement.java:4121) ~[ojdbc8-19.9.0.0.jar:19.9.0.0.0]
Currently, only solution for Hibernate version 5.4.27.Final is to wrap parameter with conversion function with required type:
select * from ADDRESS a where (TO_NUMBER(?) is null OR a.id = TO_NUMBER(?))
For Hibernate version 6.0.0.Alpha6 I didn't find working workaround yet.